1. Spring简介
一句话概括Spring,Spring是一个开源的、轻量的管制反转(IOC)和面向切面编程(AOP)的框架。
1.1 Spring、SpringMVC、Spring Boot和Spring Cloud的区别
SpringMVC是在Spring根底之上联合了MVC三层架构的框架,应用DispatchServlet和视图解析器做到了更不便的视图跳转和控制器调度解决,次要负责解决WEB利用开发。SpringMVC是Spring的一个子模块。
SpringBoot则应用了约定大于配置的理念,简化了Spring配置,专一于微服务的开发。
SpringCloud专一于全局微服务的整合、治理。
2. 管制反转—IOC
2.1 基本概念
IOC:管制反转,是一种设计思维。即将对象创立的控制权转移给了Spring容器。
假如咱们创立了类A,类A中有个成员变量类B。
- 传统开发:咱们须要手动创立类B的对象,将他赋给类A的对象,这叫被动管制对象的创立;
- IOC:Spring创立好B对象,而后存储到一个容器外面,当A对象须要应用B对象时,Spring就从寄存对象的那个容器外面取出A要应用的那个B对象。
应用传统形式,A对B产生了依赖,也就是A和B之间存在一种耦合关系(假如B的实现有了改变,咱们可能还须要改变A的源码),而通过IOC的思维,这种依赖关系就被容器取代了,所有对象的创立依赖于容器,在启动的时候,Spring会读取配置文件,并将创立的对象先注入容器,之后咱们想要应用间接从容器中取就能够。
DI:依赖注入,能够了解为是Spring实现IOC的具体形式。
2.2 Spring创建对象形式
class UserService { private UserDao dao; public void setUserDao(UserDao dao) { this.userDao = dao; }}class UserDao { private String name; public void setName(String name) { this.name = name; }}
2.2.1 构造函数
<bean id="dao" class="com.dong.dao.UserDao"> <!--应用name定位构造函数--> <constructor-arg name="name" value="dongjh"></constructor-arg></bean><bean id="service" class="com.dong.service.UserService"> <!--应用type定位构造函数--> <constructor-arg type="com.dong.dao.UserDao" ref="dao"></constructor-arg></bean>
注:
- 如果某类有无参构造方法,则默认调用无参构造方法;
- 如果某类只有一个构造方法,则默认调用该构造方法。
2.2.2 property(调用set办法创立)
<bean id="dao" class="com.dong.service.UserDao"> <!--根本类型应用value注入属性--> <property name="name" value="dongjh"></property></bean><bean id="service" class="com.dong.service.UserService"> <!--援用类型应用ref属性--> <property name="userDao" ref="dao"></property></bean>
注:
- 应用属性注入时,容器会默认调用无参构造方法创立实例,所以须要保障要创立的bean有无参的构造方法;
- 应用属性注入等价于调用了
setXXX
办法,XXX是property标签中的name,假如下面的代码的setName
办法改成了setName1
,则第一个bean的property需这样写:
<property name="name1" value="dongjh"></property>
2.3 Bean的主动拆卸(autowiring)
主动拆卸即Spring在上下文中主动寻找,主动给bean拆卸属性。
bean的主动拆卸次要有三种策略:
byname
:把与某bean(A)属性具备雷同名字的其余bean(B)主动拆卸到该bean(A)的属性中;byType
:把与某bean(A)属性具备雷同类型的其余bean(B)主动拆卸到该bean(A)的属性中;constructor
:把与某bean(A)的结构器入参具备雷同类型的其余bean(B)主动拆卸到该bean(A)的属性中;
2.3.1 byName
xml配置形式
<!--因为该bean的id为userDao,所以会间接主动拆卸上面service的userDao属性中--><bean id="userDao" class="com.dong.service.UserDao"> <property name="name" value="dongjh"></property></bean><!--该类有setUserDao办法,且设置了属性主动拆卸byName--><bean id="service" class="com.dong.service.UserService" autowired="byName"></bean>
2.3.2 byType
xml配置形式
<!--该bean的类型为com.dong.service.UserDao,所以会间接主动拆卸上面service的userDao属性中--><bean id="userDao111" class="com.dong.service.UserDao"> <property name="name" value="dongjh"></property></bean><!--该类一个com.dong.service.UserDao类型的属性,且设置了属性主动拆卸byType--><bean id="service" class="com.dong.service.UserService" autowired="byType"></bean>
2.4 Spring注解配置
JDK1.5开始反对注解,而Spring是2.5开始反对。
应用Spring注解配置须要先导入束缚: context名称空间和注解扫描。
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--注:只会寻找在同一个利用上下文中定义的注解, 比方将这个申明搁置到DispatcherServlet的配置上下文中, 它就只会查看controllers的上下文,而不会查看services的上下文, 详情见https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-annotation-config--> <context:annotation-config/> <!--指定注解扫描返回--> <context:component-scan base-package="com.dong.dao"></context:component-scan></beans>
@Autowired
之后在须要主动拆卸的属性或set办法上应用@AutoWired注解,就能够实现主动拆卸。
//具体的可润饰范畴@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
注:@Autowired会先应用byType,再按byName的程序主动拆卸。
@Autowired有一个罕用属性:required,默认值为true,这个属性示意润饰的成员变量是否肯定须要拆卸一个bean:
- 当required设置为true时,示意必须有一个能够主动拆卸到该成员变量的bean,否则报错;
- 当required设置为false时,则示意该成员变量能够为null;
@Qualifier
当有多个bean满足主动拆卸条件时,能够应用这个指定拆卸的bean,个别配合@Autowired应用。
@Qualifier有个属性value,给这个value设置想要拆卸的bean的id即可。
@Component
润饰类,示意将该类的bean对象注入到Spring容器中。
@Component有个属性value,示意注入时设置的id。
如:
//等价于<bean id="user" class="User">@Component(value="user")public class User {}
@Component有一些衍生注解,如Web开发会依照MVC架构分层,依据不同的性能层,衍生出了一些注解:
- @Repository:针对dao层
- @Service:针对service层
- @Controller:针对controller层
这四个注解性能统一,都示意将某类的bean对象注入到Spring容器中。
@Value
给属性注入值,次要用于给成员变量注入值,简略的能够注入根本类型的值,简单时能够注入一些操作系统属性或其余bean的属性。
@Scope
用于设置bean的作用域,先介绍最常见的:
- singleton:全局惟一,单例;
- prototype:每次注入创立一个新的对象。
注解和配置的区别
xml性能更加弱小,方便管理保护。注解实用于简略的属性注入。
2.5 JavaConfig配置
Spring 3.0
之后开始反对了一种更优的配置形式:基于Java类的配置形式。通过减少@Configuration注解表明这是一个配置类,其底层为@Component(即该类的bean对象也会被Spring容器接管)。
@Configuration联合@Bean注解能够实现XML配置实现注入的性能,成果与
<beans> <bean>...</bean> <bean>...</bean></beans>
相似,在应用@Bean注解时,示意将该办法返回的对象加载进Spring容器,在应用@Bean润饰办法时须要留神:
- 办法带返回值,且返回类型为想要被治理的bean的类型;
- 办法名即为默认的bean name,可应用@Bean的name属性自定义;
- 办法可带参数,则此时传入的参数也必须是被Spring容器治理的bean。
以创立UserService和UserDao为例:
@Configurationpublic class UserConfiguration { @Bean public UserService userService() { return new UserService(); } @Bean public UserDao userDao() { return new UserDao(); }}
当Spring扫描该类时,会主动将UserConfiguration、UserService、UserDao的bean注入到Spring容器中进行托管。
3. 面向切面编程—AOP
3.1 基本概念
在软件开发中,咱们须要编写许多的业务代码,但还有很多代码是业务无关的,如
- 日志性能
- 缓存清理
- 资源回收
- 权限查看
- …
为了实现业务代码和非业务代码的拆散,Spring利用代理模式抽出了非业务代码,造成了AOP思维。
AOP全称Aspect Oriented Programming,意为面向切面编程,假如业务代码自身是一条纵向的逻辑流,咱们在其中找几个点(切点,Pointcut),插入非业务代码,就像是一个个切面(Aspect),这就是一个简略的AOP例子。
切面和切点很容易被混同,严格来说,切点是切面的一部分信息,切面次要蕴含两局部信息:
- 切入点;
- 切入后要执行的动作(Advice)。
而在具体的实现中,Spring为切面还加了一个信息:在切点的哪个阶段执行?由此衍生出有:
- 前置告诉(Before):在指标办法被调用之前调用告诉性能;
- 后置告诉(After):在指标办法被调用之后调用告诉性能;
- 返回告诉(After-returning):在指标办法胜利执行之后调用告诉;
- 异样告诉(After-throwing):在指标办法抛出异样后调用告诉;
- 盘绕告诉(Around):告诉包裹了被告诉的办法,在被告诉的办法调用之前和调用之后执行自定义的行为。
对于AOP在Spring的实现有三种形式:
- 实现原生的Spring API接口
- 自定义切面类
- 注解
3.2 实现原生Spring API接口
Spring提供了诸如MethodBeforeAdvice、AfterAdvice、AfterReturningAdvice等原生接口,咱们能够通过实现这些接口构建切面,如
@Componentpublic class Log implements MethodBeforeAdvice { /** * @param method 要执行的指标对象办法 * @param objects 参数 * @param o 指标对象 * @throws Throwable */ public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println(o.getClass().getName() + "-" + method.getName() + "将要执行"); }}
该Log类实现了MethodBeforeAdvice,成为了一个在办法执行前插入的切面,其次,咱们须要在XML中进行配置,设置切点并建设其和切面的连贯:
<aop:config> <!--切入点:execution表达式,execution(要执行的办法!)--> <aop:pointcut id="pointcut" expression="execution(* com.dong.service.UserServiceImpl.*(..))"/> <aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor></aop:config>
execution示意在办法执行时触发;*示意办法返回值能够是任意类型,com.dong.service.UserServiceImpl.*应用全限定类名和办法名指定要增加前置告诉的办法,应用*示意对com.dong.service.UserServiceImpl类下的所有办法都执行切入;(..)示意办法的参数列表,应用(..)示意办法的入参能够是任意类型。
须要留神,Log必须是一个被Spring容器治理的bean对象,否则Spring将无奈找到该切面的bean,因而应用@Component注解润饰(也能够应用XML配置等形式注入)。
3.3 自定义切面类
自定义切面不须要实现Spring的原生接口,首先须要定义一个切面类,并将加载给Spring容器进行托管。
@Component(value="diy")public class DiyPointCut { public void before() { System.out.println("办法执行前"); } public void after() { System.out.println("办法执行后"); }}
与3.2类似,该形式须要通过XML配置设置切点以及其和切面的连贯。
<aop:config> <!--自定义切面,ref:要援用的bean--> <aop:aspect ref="diy"> <aop:pointcut id="pointcut" expression="execution(* com.dong.service.UserServiceImpl.*(..))"/> <!--指定advice和切点的关系--> <aop:before method="before" pointcut-ref="pointcut"></aop:before> <aop:after method="after" pointcut-ref="pointcut"></aop:after> </aop:aspect></aop:config>
3.4 注解
3.4.1 启用注解性能
首先,应用注解有两种形式:
XML导入配置:
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--启用注解--> <context:annotation-config/>、 <!--注解包扫描--> <context:component-scan base-package="com.dong.dao"></context:component-scan> <context:component-scan base-package="com.dong.log"></context:component-scan> <!--启用aop注解--> <aop:aspectj-autoproxy proxy-target-class="true"/></beans>
应用
@EnableAspectJAutoProxy
注解须要被切的类@Component(value = "userService")@EnableAspectJAutoProxypublic class UserServiceImpl implements UserService {}
其次,应用注解时,须要留神两局部:
- 申明切面:申明某类是切面以及切入后理论要执行的动作(Advice);
- 定位切点:须要通过execution表达式定位须要切入的办法。
上面介绍一下注解的应用形式。
3.4.2 注解应用
只有对一个类应用@Aspect注解润饰,就表明这是一个切面。能够对其中的办法应用
- @Before
- @After
- @AfterReturning
- @Around
- @AfterThrowing
注解指定切入机会,切入点通过execution表达式指定,举例如下:
@Aspect@Componentpublic class CustomPointCut { @Before("execution(* com.dong.service.UserServiceImpl.*(..))") public void before() { System.out.println("aop:办法产生前"); }}
4. 参考资料
- Spring最新5残缺教程
- Spring入门(十):Spring AOP应用解说
- Spring AOP - 注解形式应用介绍