文章目录
- 前言
- 一、控制反转(IOC)
- 1. 底层原理
- 2. 两种实现方式(接口)
- 3. bean管理(基于xml方式)
- 4. bean管理(基于注解方式)
- 二、面向切面编程(AOP)
- 1. 底层逻辑
- 2. 基于注解开发
前言
虽然不懂消化原理也不妨碍消化食物,但还是对消化原理感兴趣。
一、控制反转(IOC)
控制反转,就是把创建对象和对象之间的调用过程交给spring进行管理,以降低代码之间的耦合度。
1. 底层原理
控制反转是基于:xml解析、工厂模式和反射三部分实现的。
(1)创建对象及对象间调用的相关信息配置在配置文件中,spring通过xml解析获取相关信息;
(2)通过工厂模式将调用的类与被调用的类解耦;
(3)在工厂类中,使用获取到的配置信息,通过反射创建对象并设置属性;
2. 两种实现方式(接口)
(1)BeanFactory:加载配置文件时不创建对象,获取对象(使用)时才创建;
(2)ApplicationContext:BeanFactory的子接口,加载配置文件的时候就会创建对象,把耗费时间的步骤放在服务器启动的时候;
3. bean管理(基于xml方式)
(1)创建对象
- 使用bean标签注入创建对象,默认调用无参的构造函数
(2)注入属性
- set方法注入属性
- 有参构造函数注入属性
- p名称空间注入(了解)
- 使用null标签注入空值
- 特殊字符的注入可写到<![CDATA[]]>中
- 通过ref注入外部bean
- 使用array标签、list标签、map标签(内部使用entry标签)、set标签注入集合
- 使用util名称空间(如
<util:list>
)提取重复注入部分
(3)工厂bean
- 区别
普通bean:在bean标签中定义什么类型,就返回什么类型;
工厂bean:定义的类型和返回的类型可以不一致; - 实现
创建类,实现FactoryBean接口,并定义泛型;
实现getObject方法;
(4)bean的作用域(单实例或多实例)
通过bean标签中的scope属性设置作用域,默认是单实例(singleton,加载配置文件时创建对象),还可以设置为多实例(property,调用getBean时才创建对象)、请求(request,了解即可)、会话(session,了解即可)
(5)bean生命周期
配置后置处理器可在创建对象过程中加入一些自定义逻辑,如属性修改、依赖注入增强等。注意:
配置后置处理器之后,默认会为当前配置文件中的所有bean都配置上后置处理器
- 通过无参构造创建bean实例;
- 设置属性值以及对其他bean的引用
- 把bean实例传给后置处理器的postProcessBeforeInitialization方法(实现BeanPostProcessor接口)
- 调用bean的初始化方法(通过bean标签中的init-method属性设置)
- 把bean实例传给后置处理器的postProcessAfterInitialization方法(实现BeanPostProcessor接口)
- 获取到bean对象,并使用
- 当容器关闭时(手动调用context的close方法),调用bean的销毁方法(通过bean标签中的destroy-method属性设置)
(6)自动装配
在bean标签中,通过autowire配置自动装配方式,根据属性名称(byName)或者属性类型(byType)
(7)引入外部属性文件
比如数据库的配置可以放到properies为后缀的文件中,通过context名称空间引入外部属性文件,然后在设置属性值时,通过${}获取相应配置
4. bean管理(基于注解方式)
(1)什么是注解
注解是代码中的特殊标记,基本模式为@注解名称(属性名称=属性值),可以作用在类、方法、属性上。使用注解的目的是简化xml配置。
(2)开启组件扫描
可配置只扫描或者不扫描某种注解。
- 可以使用命名空间context,并设置base-package的属性值
- 也可以使用完全注解开发
@Configuration
@ComponentScan(basePackages = {"com.xxx"})
public class SpringConfig {
}
(3)创建对象的注解
功能都是一样的,只是习惯加以区分。value的属性值可以不写,默认是将类名称的首字母改成小写
- @Component(value=“xxx”):用于普通的类对象
- @Controller(value=“xxx”):一般用在web层
- @Service(value=“xxx”):一般用在业务逻辑层
- @Repository(value=“xxx”):一般用在数据访问层(DAO层)
(4)注入属性的注解
- @Autowired:根据属性类型注入类对象,属于spring的包
- @Qualifier(value=xxx):根据属性名称注入类对象,属于spring的包
需要和@Autowired一起使用 - @Resource(name=xxx):根据类型/名称注入类对象,属于javax的包
- @Value(value=xxx):注入普通类型属性
二、面向切面编程(AOP)
不修改原有代码的前提下,在主干逻辑中添加新功能,降低业务逻辑各部分之间的耦合度,比如为登录功能加上权限控制。
1. 底层逻辑
(1)有接口,使用JDK动态代理
代理对象和目标对象都实现同一个接口,代理对象方法内部调用目标对象的相应方法。代码来自
// 目标接口
public interface OrderService {
void add();
}
// 目标类
public class OrderServiceImpl implements OrderService {
@Override
public void add() {
System.out.println("添加用户");
}
}
// 代理类
public class OrderServiceProxy implements OrderService {
@Override
public void add() {
long begin = System.currentTimeMillis();
OrderService.add();
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - begin)+"毫秒");
}
}
代码实现使用了java.lang.reflect.Proxy的newProxyInstance方法。第一个参数:类加载器;第二个参数:接口类型;第三个参数:调用得到处理器代码来自
public class ProxyHandler implements InvocationHandler {
private Object targetObject;//被代理的对象
public Object newProxyInstance(Object targetObject){
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
}
//该方法在代理对象调用方法时调用
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("记录日志");
return method.invoke(targetObject,args);
}
}
(2)没有接口,使用CGLIB动态代理
代理对象继承目标类,重写相应的方法,并使用super.xxx()调用父类方法
// 目标类
public class OrderService {
public void add() {
System.out.println("添加用户");
}
}
// 代理类
public class OrderServiceProxy extends OrderService {
@Override
public void add() {
long begin = System.currentTimeMillis();
super.add();
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - begin)+"毫秒");
}
}
2. 基于注解开发
(1)操作术语
- 连接点:类中可以被增强的方法
- 切入点:实际被增强的方法
- 通知(增强):实际增强的逻辑部分
- 切面:是动作,把通知应用到切入点的过程
(2)使用方法
- 开启组件扫描(@ComponentScan),开启生成代理对象(@EnableAspectJAutoProxy(proxyTargetClass = true))
- 创建类,在类中写主干逻辑
- 创建增强类,编写增强逻辑,在类上加@Aspect注解(用于标识一个类为切面)
(3)五种通知类型
前置(@Before)、后置(@AfterReturning有异常不执行)、环绕(@Around环绕之后有异常不执行)、异常(@AfterThrowing)、最终(@After)
切入点表达式为execution([权限修饰符][返回类型][类全路径][方法名称](参数列表)),权限修饰符:public/private/protected,一般直接写*,表示不限制;返回类型可以不写。
// 配置类
@Configuration
@ComponentScan(basePackages={"com.atguigu"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop() {}
// 主干逻辑
@Component
public class User {
public void add() {
System.out.println("add主逻辑");
}
}
// 增强类
// 多个增强类对同一个方法进行增强,设置优先级,数字越小优先级越高
@Order(1)
@Aspect
@Component
public class UserProxy{
// 相同切入点的抽取
@Pointcut(value="execution(* com.atguigu.dao.BookDao.add(..))")
public void pointdemo() {}
// 对com.atguigu.dao.BookDao类里的add方法进行增强
@Before(value="execution(* com.atguigu.dao.BookDao.add(..))")
public void before() {
System.out.println("增强的逻辑");
}
@After(value="pointdemo()")
public void after() {
System.out.println("增强的逻辑");
}
}