2021-04-08

Spring介绍

Spring 是一个开源框架,是一个分层的 JavaEE 一站式框架。


所谓一站式框架是指 Spring 有 JavaEE 开发的每一层解决方案。


WEB层:SpringMVC


Service层:Spring的Bean管理,声明式事务


DAO层:Spring的JDBC模板,ORM模板


优点:


IOC:方便解耦合


AOP:对程序进行扩展


轻量级框架


方便与其他框架整合


Spring使用

Spring开发包解压后的目录介绍:


docs: Spring 开发规范和API


libs: Spring jar 包和源代码


schema: Spring 配置文件的约束





DataAccess 用于数据访问,WEB 用于页面显示,核心容器也就是IOC部分。



控制反转(IOC)

控制反转(Inversion of Control)是指将对象的创建权反转(交给)Spring。


使用IOC就需要导入IOC相关的包,也就是上图中核心容器中的几个包:beans,context,core,expression四个包。



实现原理

传统方式创建对象:


UserDAO userDAO=new UserDAO();


进一步面向接口编程,可以多态:


UserDAO userDAO=new UserDAOImpl();


这种方式的缺点是接口和实现类高耦合,切换底层实现类时,需要修改源代码。程序设计应该满足OCP元祖,在尽量不修改程序源代码的基础上对程序进行扩展。此时,可以使用工厂模式:


class BeanFactory{


    public static UserDAO getUserDAO(){


        return new UserDAOImpl();


    }


}


此种方式虽然在接口和实现类之间没有耦合,但是接口和工厂之间存在耦合。


使用工厂+反射+配置文件的方式,实现解耦,这也是 Spring 框架 IOC 的底层实现。


//xml配置文件


//<bean id="userDAO" class="xxx.UserDAOImpl"></bean>


class BeanFactory{


    public static Object getBean(String id){


        //解析XML


        //反射


        Class clazz=Class.forName();


        return clazz.newInstance();


    }


}


IOC XML 开发

在 docs 文件中包含了 xsd-configuration.hmtl 文件。其中定义了 beans schema。


<beans xmlns="http://www.springframework.org/schema/beans"


    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"


    xsi:schemaLocation="


        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


    //在此配置bean


    <bean id="userService" class="x.y.UserServiceImpl">


    </bean>


</beans>


调用类:


ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");


UserService userService=(UserService)applicationContext.getBean("userService");


userService.save();


IOC 和 DI


DI 指依赖注入,其前提是必须有 IOC 的环境,Spring 管理这个类的时候将类的依赖的属性注入进来。


例如,在UserServiceImpl.java中:


public class UserServiceImpl implements UserService{


private String name;


public void setName(String name){


this.name=name;


}


public void save(){


System.out.println("save "+name);


}


}


在配置文件中:


<beans xmlns="http://www.springframework.org/schema/beans"


    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"


    xsi:schemaLocation="


        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="userService" class="spring.demo1.UserServiceImpl">


    <!--配置依赖的属性-->


    <property name="name" value="tony"/>


    </bean>


</beans>


测试代码:


@Test


public void demo2(){


//创建Spring工厂


ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");


UserService userService=(UserService)applicationContext.getBean("userService");


userService.save();


}


运行结果:


save tony


可以看到,在配置文件中配置的属性,在 Spring 管理该类的时候将其依赖的属性成功进行了设置。如果不使用依赖注入,则无法使用接口,只能使用实现类来进行设置,因为接口中没有该属性。


Spring 的工厂类


BeanFactory: 老版本的工厂类,在调用getBean()方法时,才会生成类的实例。


ApplicationContext: 在加载配置文件的时候,就会将 Spring 管理的类都实例化。有两个实现类:


ClassPathXmlApplicationContext: 加载类路径下的配置文件


FileSystemXmlApplicationContext: 加载磁盘下的配置文件


bean标签配置


id: 唯一约束,不能出现特殊字符


name: 理论上可以重复,但是开发中最好不要。可以出现特殊字符


生命周期:


init-method: bean被初始化的时候执行的方法


destroy-method: bean被销毁的时候执行的方法


作用范围:


scope: bean的作用范围,有如下几种,常用的是前两种


singleton: 默认使用单例模式创建


prototype: 多例


request: 在web项目中,spring 创建类后,将其存入到 request 范围中


session: 在web项目中,spring 创建类后,将其存入到 session 范围中


globalsession: 在web项目中,必须用在 porlet 环境


属性注入设置


构造方法方式的属性注入: Car 类在构造方法中有两个属性,分别为 name 和 price。


<bean id="car" class="demo.Car">


    <constructor-arg name="name" value="bmw">


    <constructor-arg name="price" value="123">


</bean>


set 方法属性注入: Employee 类在有两个 set 方法,分别设置普通类型的 name 和引用类型的 Car (使用 ref 指向引用类型的 id 或  name)。


<bean id="employee" class="demo.Employee">


    <property name="name" value="xiaoming">


    <property name="car" ref="car">


</bean>


P名称空间的属性注入: 首先需要引入p名称空间:


<beans xmlns="http://www.springframework.org/schema/beans"


    //引入p名称空间


    xmlns:p="http://www.springframework.org/schema/p"


    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"


    xsi:schemaLocation="


        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


</beans>


如果是普通属性:


<bean id="car" class="demo.Car" p:name="bmv" p:price="123">


</bean>


如果是引用类型:


<bean id="employee" class="demo.Employee" p:name="xiaoming" p:car-ref:"car">


</bean>


SpEL(Spring Expression Language)属性注入(Spring 3.x以上版本)


<bean id="car" class="demo.Car">


    <property name="name" value="#{'xiaoming'}">


    <property name="car" ref="#{car}">


</bean>


集合类型属性注入:


<bean id="car" class="demo.Car">


    <property name="namelist">


        <list>


            <value>qirui</value>


            <value>baoma</value>


            <value>benchi</value>


        </list>


    </property>


</bean>


多模块开发配置


在加载配置文件的时候,加载多个配置文件


在一个配置文件中引入多个配置文件,通过实现


IOC 注解开发

示例


引入jar包: 除了要引入上述的四个包之外,还需要引入aop包。


创建 applicationContext.xml ,使用注解开发引入 context 约束(xsd-configuration.html)


<beans xmlns="http://www.springframework.org/schema/beans"


    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"


    xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="


        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd


        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">


        <!-- bean definitions here -->


</beans>


组件扫描: 使用IOC注解开发,需要配置组件扫描,也就是哪些包下的类使用IOC的注解。


<context:component-scan base-package="demo1">


在类上添加注解


使用注解设置属性的值


属性如果有set方法,将属性注入的注解添加到set方法


属性没有set方法,将注解添加到属性上。


@Component("UserDao")//相当于配置了一个<bean> 其id为UserDao,对应的类为该类


public class UserDAOImpl implements UserDAO {


@Override


public void save() {


// TODO Auto-generated method stub


System.out.println("save");


}


}


注解详解


@Component


组件注解,用于修饰一个类,将这个类交给 Spring 管理。


有三个衍生的注解,功能类似,也用来修饰类。


@Controller:修饰 web 层类


@Service:修饰 service 层类


@Repository:修饰 dao 层类


2.属性注入


普通属性使用 @Value 来设置属性的值


对象属性使用 @Autowired  ,这个注解是按照类型来进行属性注入的。如果希望按照 bean 的名称或id进行属性注入,需要用 @Autowired 和 @Qualifier 一起使用


实际开发中,使用 @Resource(name=" ") 来进行按照对象的名称完成属性注入


3.其他注解


@PostConstruct 相当于 init-method,用于初始化函数的注解


@PreDestroy 相当于 destroy-method,用于销毁函数的注解


@Scope 作用范围的注解,常用的是默认单例,还有多例 @Scope("prototype")


IOC 的 XML 和注解开发比较


适用场景:XML 适用于任何场景;注解只适合自己写的类,不是自己提供的类无法添加注解。


可以使用 XML 管理 bean,使用注解来进行属性注入


AOP开发

AOP 是 Aspect Oriented Programming 的缩写,意为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,是OOP的延续。


AOP 能够对程序进行增强,在不修改源码的情况下,可以进行权限校验,日志记录,性能监控,事务控制等。


也就是说功能分为两大类,一类是核心业务功能,一类是辅助增强功能。两类功能彼此独立进行开发。比如登录功能是核心业务功能,日志功能是辅助增强功能,如果有需要,将日志和登录编制在一起。辅助功能就称为切面,这种能选择性的、低耦合的把切面和核心业务功能结合的编程思想称为切面编程。


底层实现


JDK 动态代理只能对实现了接口的类产生代理。Cglib 动态代理可以对没有实现接口的类产生代理对象,生成的是子类对象。


使用 JDK 动态代理:


public interface UserDao {


public void insert();


public void delete();


public void update();


public void query();


}


实现类:


public class UserDaoImpl implements UserDao { @Override public void insert() { System.out.println("insert"); } @Override public void delete() { System.out.println("delete"); } @Override public void update() { System.out.println("update"); } @Override public void query() { System.out.println("query"); } }


JDK 代理:


public class JDKProxy implements InvocationHandler{


private UserDao userDao;


public JDKProxy(UserDao userDao){


this.userDao=userDao;


}


public UserDao createProxy(){


UserDao userDaoProxy=(UserDao)Proxy.newProxyInstance(userDao.getClass().getClassLoader(),


userDao.getClass().getInterfaces(), this);


return userDaoProxy;


}


@Override


public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {


if("update".equals(method.getName())){


System.out.println("权限校验");


return method.invoke(userDao, args);


}


return method.invoke(userDao, args);


}


}


通过动态代理增强了 update 函数。 测试类:


public class Demo1 {


@Test


public void demo1(){


UserDao userDao=new UserDaoImpl();


UserDao proxy=new JDKProxy(userDao).createProxy();


proxy.insert();


proxy.delete();


proxy.update();


proxy.query();


}


}


运行结果为:


insert


delete


权限校验


update


query


CglibCglib 是第三方开源代码生成类库,可以动态添加类的属性和方法。


与上边JDK代理不同,Cglib的使用方式如下:


public class CglibProxy implements MethodInterceptor{


//传入增强的对象


private UserDao customerDao;


public CglibProxy(UserDao userDao){


this.userDao=userDao;


}


public UserDao createProxy(){


Enhancer enhancer=new Enhancer();


enhancer.setSuperclass(userDao.getClass());


enhancer.setCallback(this);


UserDao proxy=(UserDao)enhancer.create();


return proxy;


}


@Override


public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {


if("save".equals(method.getName())){


System.out.println("enhance function");


return methodProxy.invokeSuper(proxy, args);


}


return methodProxy.invokeSuper(proxy, args);


}


}


如果实现了接口的类,底层采用JDK代理。如果不是实现了接口的类,底层采用 Cglib代理。


IOC与传统方式的比较


获取对象方式:传统通过 new 关键字主动创建一个对象。IOC 方式中,将对象的生命周期交给 Spring 管理,直接从 Spring 获取对象。也就是控制反转————将控制权从自己手中交到了 Spring 手中。


Spring 的 AOP 开发(AspectJ 的 XML 方式)

AspectJ 是一个 AOP 的框架,Spring 引入 AspectJ,基于 AspectJ 进行 AOP 的开发。


相关术语


Joinpoint: 连接点,可以被拦截到的点。也就是可以被增强的方法都是连接点。


Pointcut: 切入点,真正被拦截到的点,也就是真正被增强的方法


Advice: 通知,方法层面的增强。对某个方法进行增强的方法,比如对 save 方法进行权限校验,权限校验的方法称为通知。


Introduction: 引介,类层面的增强。


Target: 目标,被增强的对象(类)。


Weaving: 织入,将 advice 应用到 target 的过程。


Proxy: 代理对象,被增强的对象。


Aspect: 切面,多个通知和多个切入点的组合。


使用方法


引入相关包


引入配置文件


<?xml version="1.0" encoding="UTF-8"?>


<beans xmlns="http://www.springframework.org/schema/beans"


    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"


    xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="


        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd


        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here -->


</beans>

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,137评论 6 511
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,824评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,465评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,131评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,140评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,895评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,535评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,435评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,952评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,081评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,210评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,896评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,552评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,089评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,198评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,531评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,209评论 2 357

推荐阅读更多精彩内容

  • 作者:Java架构研究室 原文链接:https://mp.weixin.qq.com/s?__biz=MzU0NT...
    符文杰9527阅读 482评论 0 0
  • 好记忆不如烂笔头, 能记下点什么, 就记下点什么, 方便后期的巩固 Spring介绍 Spring 是一个开源框架...
    零点145阅读 210评论 0 0
  • SSM框架搭建 1.新建一个Maven项目 打开IDEA,然后File...
    搁搁搁搁搁浅阅读 635评论 0 1
  • springAop:面向切面的编程 应用场景:权限控制、事物管理、日志打印等等,就是在不同的方法中重复用到相同的代...
    HJJ_3c00阅读 334评论 0 0
  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,567评论 0 11