Spring基础
IOC
控制反转和依赖注入
控制反转即组件的调用方不参入组件的创建配置和管理.而是将这些任务交给第三方去做即IOC容器。使得获取依赖组件的方式由自身获取(new对象)变成为IOC容器的主动注入。
依赖注入是Spring的一种编程方式,当组件A依赖于组件B时,我们就可以把组件B设置成A的成员变量来用IOC容器主动注入。
两者在本质上是一样的。
为什么要使用依赖注入
主要原因: 通过new的方式为成员变量进行赋值,这时主业务与依赖的组件类深度绑定(属于强耦合),这样的代码对于更新来说非常困难(这样对主业务进行修改不符合开闭原则)。使用依赖注入的思想可以解耦合,使得程序变得容易维护。
IOC的使用
自动配置
Spring的自动装配是基于"组件扫描"和"自动装配"这两大技术。组件扫描来查找@Component注解,对发现的java类进行装配。通过@Autowired注解进行自动装配。
自动配置这种方式是配置javaBean的首选
@Configuration //该标签说明该类是一个java配置类
@ComponentScan //开启了自动扫描,可以指定需要扫描的包名。如果没有指定包名,则会扫描该配置类所在的包以及子包
public class Config {
}
//该注解会将该类标记为IOC容器中的一个bean,当扫描到该类时,会将其添加到IOC容器中去
@Component
public class Student {
private Person person;
public Student(){}
//自动装配注解
@Autowired
public Student(Person person){
this.person = person;
}
}
/*
该注解一般可以加在字段、set方法和构造器上。无论添加在哪里,它都会在ioc中根据需要注入的类型来寻找合适的bean来进行注入。
当没有找到合适的类型时,就会报错。当有多个符合的类型bean时,则需要解决冲突,否则报错。
当添加在字段上时底层使用反射进行注入。添加在方法上时,调用方法进行注入。
*/
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
// 当没有合适的类型注入时,默认true会报错,当设置为false时,将不会报错,而是直接赋值为null。
boolean required() default true;
}
当出现冲突时,可以使用@Qualifier注解进行解决。
/*
当该注解使用在set方法、构造器或者字段上时,需要和@Autowired注解一起使用。这样就可以设置value属性来限定想要注入的bean。
当该注解在类上使用时,需要将该注解和@Component注解组合使用。这样就可以设置value属性来为该类设置限定符。当没有设置value属性或者没有设置@Qualifier属性时,默认限定符为第一个字母小写的类名。
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
String value() default ""; //设置限定符
}
java配置
当不能在源码上添加注解时,就不能进行自动配置了。这时我们就应该使用基于java的配置方式。
@Configuration //该注解标记该类是一个基于java的配置类
@ComponentScan
public class Config {
/*
该注解表示此函数的返回值将会被加入到ioc容器中,成为一个bean。id为里面的字符串,默认值为方法名。
*/
@Bean("BeanName")
public Person getPerson(){ //若该方法里面有参数,方法的调用方Spring框架会进行自动装配.出现冲突可在值的前面加上注解@Qualifier进行解决
return new Person();
}
}
需要注意的是当该配置类的其他方法调用@Bean注解的方法来满足自身依赖时,默认情况下该方法会被拦截并且不会执行该方法而是直接返回一个当初创建的bean(默认情况下bean是单例的)
xml配置
<!--通过bean标签来将类加入到IOC容器中去 class属性为类的全类名, id属性指定class的id。-->
<bean id="s" class="SpringIOC.Student">
<!-- 通过set方法向成员变量(八种基本类型 + String)注入。 name为属性名 value为注入的值-->
<property name="name" value="rtf"></property>
<!-- 通过set方法向自定义成员变量注入 ref标签中的bean值就是需要注入bean的id值-->
<property name="ref">
<ref bean="beanNaem"/>
</property>
<!--通过该标签使得用构造方法进行属性的注入,一个该标签代表一个参数,用于区分构造方法的重载。
当不能区分时可以在该标签中添加type属性设置属性的类型辅助区分重载 -->
<constructor-arg>
<value></value>
<!--这里和set注入写的标签相同-->
</constructor-arg>
混合配置
在javaConfig中引入javaConfig用@Import(javaConfigName.class)
在javaConfig中引入xml用@ImportResource("xmlName.xml")
在xml中引入xml配置文件用<import resource = "xmlName.xml>
在xml中引入javaConfig可直接在xml文件中将javaCong设置为javaBean即可
Spring对bean生命周期的管理
bean的生命周期是指bean被创建(初始化)、使用到销毁的过程。
当bean的产生是单例的时,它的生命周期由Spring框架来管理。如果不是单例则不受SPringle框架管理。
1、初始化阶段
1)调用bean的构造函数,创建实例;
2)进行参数依赖注入;
3)若实现org.springframework.beans.BeanNameAware接口,则调用BeanNameAware的setBeanName()方法;
4)若实现org.springframework.beans.factory.BeanClassLoaderAware接口,则调用BeanClassLoaderAware的setBeanClassLoader()方法;
5)若实现org.springframework.context.ApplicationContextAware接口,则调用ApplicationContextAware的setApplicationContext()方法;
6)若使用了注解@PostConstruct,则调相应方法;
7)若实现org.springframework.beans.factory.InitializingBean接口,则调用InitializingBean接口的afterPropertiesSet方法;
8)若bean定义的使用了initMethod,则调相应方法;
9)若实现org.springframework.beans.factory.config.BeanPostProcessor接口,则调用BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInitialization方法;
2、使用阶段
1)bean在项目的使用;
3、销毁阶段
当销毁bean存放的应用上下文时:
1)若使用注解@PreDestroy,则调用相应方法;
2)若bean定义中配置了destroyMethod,则调用相应方法;
3)若实现org.springframework.beans.factory.DisposableBean接口,则调用DisposableBean接口的destroy方法;
AOP
什么是AOP、AOP的好处
AOP即面向切面编程。AOP将通过动态代理技术实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP可以实现核心功能与辅助功能的分离,达到解耦合的作用便于代码的维护并消除了重复代码的编写。
代理技术
动态代理
JDK动态代理
public Object jdkProxy(Object o) {
Object proxy = Proxy.newProxyInstance(JdkProxy.class.getClassLoader(),
o.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("---jdk log---");
Object ret = method.invoke(o, args);
return ret;
}
});
return proxy;
}
CGLIB动态代理
public Object cglibProxy(Object obj){
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(CglibProxy.class.getClassLoader());
enhancer.setSuperclass(obj.getClass());
MethodInterceptor interceptor = new MethodInterceptor(){
@Override
public Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
System.out.println("---cglib log---");
Object ret = method.invoke(obj, objects);
return ret;
}
};
enhancer.setCallback(interceptor);
Object proxy = enhancer.create();
return proxy;
}
AOP术语
通知:通知定义了切面要完成的任务以及何时执行该任务(什么和何时)。
连接点:程序执行过程中能够插入切面的点。这些点可以是调用方法时、抛出异常时甚至是修改字段时(何处)。
切点:切点就是实际上需要插入切面的连接点,切点的定义有助于缩小连接点的范围。
切面:通知加切点构成了切面。
织入:织入就是把切面应用到目标对象上创建代理对象的过程。
AOP的使用
注解配置
配置一个切面类
@Aspect //该注解表明该类是一个切面类。
public class Audience {
/*
该类中的方法体就是该切面的具体行为,而@Before注解表明该方法该何时使用(在原方法之前)两者构成为通知。
而注解里面的值则说明了切点。通知和切点构成了切面。
*/
@Before("execution(* SpringAOP.Perform.* (..))")
public void before(){
System.out.println("前置方法执行了");
}
}
注解 | 通知 |
---|---|
@After | 通知方法会在目标方法返回或抛出异常时调用 |
@AfterReturning | 通知方法会在目标方法返回后调用 |
@AfterThrowing | 通知方法会在目标方法抛出异常时调用 |
@Around | 通知方法会将目标方法封装起来 |
@Before | 通知方法会在目标方法调用前执行 |
基于java的配置类
@Configuration
@ComponentScan
@EnableAspectJAutoProxy //启用自动代理,如果没有开启自动代理,Audience类只会被当成一个普通的POJO
public class Config {
/*
在使用Spring的AOP时,代理对象和切面类都需要加入到IOC容器中去。
*/
@Bean
public Audience audience(){
return new Audience();
}
@Bean
public Perform perform(){
return new Perform();
}
这时从IOC容器中获取的Perfrom对象就不是本来的Perform对象了,而是Perform对象的代理对象。