IOC是Spring的灵魂。它改变了大家的编程习惯,以至于大家认为,Java编程如此简单。
Spring-IOC
IOC和DI是什么?
IOC(Inversion of Control),字面意思是控制反转。这是一种重要的架构设计原则。
一个功能实现,需要三个步骤:创建对象(多个)、组合对象、执行功能。前两个步骤,既繁琐,也不涉及功能逻辑。如果有一个中介,帮助研发人员执行前两个步骤,能显著提高研发的效率。
Spring基于IOC设计原则,创造了IOC容器。在容器的管理下,对象之间的依赖关系,变得简单而又清晰。
在互联网公司,业务和系统非常复杂,一个系统的对象多达万千,单靠人力的手动管理,几乎不可能,效率非常低下。
DI(Dependency Injection),即依赖注入。DI干的是第二件事,组合对象。
创建IOC容器
public class HelloIoc {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld obj = context.getBean("helloWorld", HelloWorld.class);
System.out.println(obj.getMessage());
}
}
public class HelloWorld implements Serializable {
private String message;
}
<?xml version="1.0" encoding="UTF-8"?>
<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-3.0.xsd">
<bean id="helloWorld" class="com.junzitaotao.ioc.hello.domain.HelloWorld">
<property name="message" value="Hello World!"/>
</bean>
</beans>
IOC创建对象
XML配置方式
public class IocCreateXmlApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("============================= start =========================================");
// 使用无参构造方法,创建对象
User noArgConstructorUser = context.getBean("noArgConstructorUser", User.class);
System.out.println("noArgConstructorUser = " + noArgConstructorUser.toString());
System.out.println("======================================================================");
User argConstructorUser = context.getBean("argConstructorUser", User.class);
System.out.println("argConstructorUser = " + argConstructorUser.toString());
System.out.println("======================================================================");
User instanceUser = context.getBean("instanceUser", User.class);
System.out.println("instanceUser = " + instanceUser.toString());
System.out.println("======================================================================");
User staticInstanceUser = context.getBean("staticInstanceUser", User.class);
System.out.println("staticInstanceUser = " + staticInstanceUser.toString());
System.out.println("============================= end =========================================");
}
}
public class User implements Serializable {
}
public class ObjectFactory {
/**
* 实例方法创建对象
*/
public User createUser() {
System.out.println("实例方法创建对象");
return new User(100L, "Tommy");
}
/**
* 静态方法创建对象
*/
public User createStaticUser() {
System.out.println("静态方法创建对象");
return new User(200L, "Green");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<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-3.0.xsd">
<!-- 无参构造方法,使用property初始化属性的值 -->
<bean id="noArgConstructorUser" class="com.junzitaotao.ioc.create.xml.domain.User">
<property name="id" value="12345"/>
<property name="name" value="Nike"/>
</bean>
<!-- 有参构造方法 -->
<bean id="argConstructorUser" class="com.junzitaotao.ioc.create.xml.domain.User">
<constructor-arg index="0" type="java.lang.Long" value="100"/>
<constructor-arg index="1" type="java.lang.String" value="Jack"/>
</bean>
<!-- 创建工厂对象 -->
<bean id="objectFactory" class="com.junzitaotao.ioc.create.xml.factory.ObjectFactory"/>
<!-- 使用工厂实例方法,创建对象 -->
<bean id="instanceUser" factory-bean="objectFactory" factory-method="createUser"/>
<!-- 使用工厂静态方法,创建对象 -->
<bean id="staticInstanceUser" factory-bean="objectFactory" factory-method="createStaticUser"/>
</beans>
Java配置方式
public class IocCreateJavaApp {
public static void main(String[] args) {
// @Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext
ApplicationContext context = new AnnotationConfigApplicationContext(UserConfiguration.class);
System.out.println("============================= start =========================================");
// 使用无参构造方法,创建对象
User noArgConstructorUser = context.getBean("noArgConstructorUser", User.class);
System.out.println("noArgConstructorUser = " + noArgConstructorUser.toString());
System.out.println("======================================================================");
User argConstructorUser = context.getBean("argConstructorUser", User.class);
System.out.println("argConstructorUser = " + argConstructorUser.toString());
System.out.println("======================================================================");
User instanceUser = context.getBean("instanceUser", User.class);
System.out.println("instanceUser = " + instanceUser.toString());
System.out.println("======================================================================");
User staticInstanceUser = context.getBean("staticInstanceUser", User.class);
System.out.println("staticInstanceUser = " + staticInstanceUser.toString());
System.out.println("============================= end =========================================");
}
}
public class User implements Serializable {
}
public class ObjectFactory {
/**
* 实例方法创建对象
*/
public User createUser() {
System.out.println("实例方法创建对象");
return new User(100L, "Tommy");
}
/**
* 静态方法创建对象
*/
public User createStaticUser() {
System.out.println("静态方法创建对象");
return new User(200L, "Green");
}
}
@Configuration
public class UserConfiguration {
@Bean("noArgConstructorUser")
public User noArgConstructorUser() {
User user = new User();
user.setId(12345L);
user.setName("Nike");
return user;
}
@Bean("argConstructorUser")
public User argConstructorUser() {
return new User(100L, "Jack");
}
@Bean("objectFactory")
public ObjectFactory objectFactory() {
return new ObjectFactory();
}
@Bean("instanceUser")
public User instanceUser(ObjectFactory objectFactory) {
return objectFactory.createUser();
}
@Bean("staticInstanceUser")
public User staticInstanceUser(ObjectFactory objectFactory) {
return objectFactory.createStaticUser();
}
}
注解配置方式
@Component
public class ComponentBean implements Serializable {}
@Repository
public class RepositoryBean implements Serializable {}
@Service
public class ServiceBean implements Serializable {}
@Controller
public class ControllerBean implements Serializable {}
public class IocCreateAnnotationApp {
public static void main(String[] args) {
// 注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext
ApplicationContext context = new AnnotationConfigApplicationContext("com.junzitaotao.ioc.create.annotation");
System.out.println("============================= start =========================================");
// 使用无参构造方法,创建对象
ServiceBean serviceBean = context.getBean("serviceBean", ServiceBean.class);
System.out.println("serviceBean = " + serviceBean.toString());
System.out.println("======================================================================");
ComponentBean componentBean = context.getBean("componentBean", ComponentBean.class);
System.out.println("componentBean = " + componentBean.toString());
System.out.println("======================================================================");
ControllerBean controllerBean = context.getBean("controllerBean", ControllerBean.class);
System.out.println("controllerBean = " + controllerBean.toString());
System.out.println("======================================================================");
RepositoryBean repositoryBean = context.getBean("repositoryBean", RepositoryBean.class);
System.out.println("repositoryBean = " + repositoryBean.toString());
System.out.println("============================= end =========================================");
}
}
IOC组合对象
XML配置方式
public class IocCombineXmlApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("============================= start =========================================");
// 使用无参构造方法,创建对象
ControllerBean controllerBean = context.getBean("controllerBean", ControllerBean.class);
System.out.println("controllerBean = " + controllerBean.toString());
System.out.println("============================= end =========================================");
}
}
<!-- 无参构造方法,使用property初始化属性的值 -->
<bean id="componentBean" class="com.junzitaotao.ioc.combine.xml.domain.ComponentBean">
</bean>
<bean id="repositoryBean" class="com.junzitaotao.ioc.combine.xml.domain.RepositoryBean">
<property name="componentBean" ref="componentBean"/>
</bean>
<bean id="serviceBean" class="com.junzitaotao.ioc.combine.xml.domain.ServiceBean">
<property name="repositoryBean" ref="repositoryBean"/>
</bean>
<!-- 有参构造方法 -->
<bean id="controllerBean" class="com.junzitaotao.ioc.combine.xml.domain.ControllerBean">
<property name="serviceBean" ref="serviceBean"/>
</bean>
Java配置方式
@Configuration
public class CombineConfiguration {
@Bean("componentBean")
public ComponentBean componentBean() {
ComponentBean componentBean = new ComponentBean();
componentBean.setId(1L);
componentBean.setName("Nike");
return componentBean;
}
@Bean("repositoryBean")
public RepositoryBean repositoryBean(ComponentBean componentBean) {
RepositoryBean repositoryBean = new RepositoryBean();
repositoryBean.setId(2L);
repositoryBean.setName("Jack");
// 组合对象
repositoryBean.setComponentBean(componentBean);
return repositoryBean;
}
@Bean("serviceBean")
public ServiceBean serviceBean(RepositoryBean repositoryBean) {
ServiceBean serviceBean = new ServiceBean();
serviceBean.setId(3L);
serviceBean.setName("Tom");
// 组合对象
serviceBean.setRepositoryBean(repositoryBean);
return serviceBean;
}
@Bean("controllerBean")
public ControllerBean controllerBean(ServiceBean serviceBean) {
ControllerBean controllerBean = new ControllerBean();
controllerBean.setId(4L);
controllerBean.setName("Green");
// 组合对象
controllerBean.setServiceBean(serviceBean);
return controllerBean;
}
}
public class IocCombineJavaApp {
public static void main(String[] args) {
// @Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext
ApplicationContext context = new AnnotationConfigApplicationContext(CombineConfiguration.class);
System.out.println("============================= start =========================================");
ControllerBean controllerBean = context.getBean("controllerBean", ControllerBean.class);
System.out.println("controllerBean = " + controllerBean.toString());
System.out.println("============================= end =========================================");
}
}
注解配置方式
@Component
public class ComponentBean implements Serializable {}
@Repository
public class RepositoryBean implements Serializable {
@Resource // 组合对象
private ComponentBean componentBean;
}
@Service
public class ServiceBean implements Serializable {
@Resource // 组合对象
private RepositoryBean repositoryBean;
}
@Controller
public class ControllerBean implements Serializable {
@Resource // 组合对象
private ServiceBean serviceBean;
}
public class IocCombineAnnotationApp {
public static void main(String[] args) {
// 注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext
ApplicationContext context = new AnnotationConfigApplicationContext("com.junzitaotao.ioc.combine.annotation");
System.out.println("============================= start =========================================");
ControllerBean controllerBean = context.getBean("controllerBean", ControllerBean.class);
System.out.println("controllerBean = " + controllerBean.toString());
System.out.println("============================= end =========================================");
}
}
对象的生命周期管理
public class Person implements BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean {
private String name;
private String address;
private int phone;
private BeanFactory beanFactory;
private String beanName;
public Person() {
System.out.println("【07】调用Person的构造器实例化(没有则调用无参的)【Person构造器】");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("【10】注入属性name【反射调用setter注入属性】");
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
System.out.println("【11】注入属性address【反射调用setter注入属性】");
this.address = address;
}
public int getPhone() {
return phone;
}
public void setPhone(int phone) {
System.out.println("【12】注入属性phone【反射调用setter注入属性】");
this.phone = phone;
}
@Override
public String toString() {
return "Person [address=" + address + ", name=" + name + ", phone=" + phone + "]";
}
// 这是BeanFactoryAware接口方法
@Override
public void setBeanFactory(BeanFactory arg0) throws BeansException {
System.out.println("【14】调用BeanFactoryAware.setBeanFactory()【BeanFactoryAware接口】");
this.beanFactory = arg0;
}
// 这是BeanNameAware接口方法
@Override
public void setBeanName(String arg0) {
System.out.println("【13】调用BeanNameAware.setBeanName()【BeanNameAware接口】");
this.beanName = arg0;
}
// 这是InitializingBean接口方法
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("【17】调用InitializingBean.afterPropertiesSet()【InitializingBean接口】");
}
// 这是DiposibleBean接口方法
@Override
public void destroy() throws Exception {
System.out.println("【24】调用DiposibleBean.destory()【DiposibleBean接口】");
}
// 通过<bean>的init-method属性指定的初始化方法
public void myInit() {
System.out.println("【18】调用<bean>的init-method属性指定的初始化方法【init-method】");
}
// 通过<bean>的destroy-method属性指定的初始化方法
public void myDestory() {
System.out.println("【25】调用<bean>的destroy-method属性指定的初始化方法【destroy-method】");
}
}
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public MyBeanFactoryPostProcessor() {
super();
System.out.println("【01】❈这是BeanFactoryPostProcessor实现类构造器!!");
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException {
System.out.println("【02】❈这是BeanFactoryPostProcessor调用postProcessBeanFactory方法");
BeanDefinition bd = arg0.getBeanDefinition("person");
bd.getPropertyValues().addPropertyValue("phone", "110");
}
}
public class MyBeanPostProcessor implements BeanPostProcessor {
public MyBeanPostProcessor() {
super();
System.out.println("【03】★这是BeanPostProcessor实现类构造器!!");
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("【19】★这是 BeanPostProcessor接口方法postProcessAfterInitialization(bean,beanName)(Object)对属性进行更改!");
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("【15】★这是BeanPostProcessor接口方法postProcessBeforeInitialization(bean,beanName)(Object)对属性进行更改!");
return bean;
}
}
public class MyInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
public MyInstantiationAwareBeanPostProcessor() {
super();
System.out.println("【04】✿这是InstantiationAwareBeanPostProcessorAdapter实现类构造器!!");
}
// 接口方法、实例化Bean之前调用
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("【05】✿这是InstantiationAwareBeanPostProcessorAdapter调用postProcess(Before)Instantiation(beanClass,beanName)(Object)方法,实例化Bean之前调用");
return null; //一般返回null,如果返回不为空,下面的bean实例化流程不会执行
}
@Override
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("【06】✿这是InstantiationAwareBeanPostProcessorAdapter调用determineCandidateConstructors方法");
return super.determineCandidateConstructors(beanClass, beanName);
}
// 接口方法、实例化Bean之后调用
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("【20】✿这是InstantiationAwareBeanPostProcessorAdapter调用postProcess(After)Initialization(bean,beanName)(Object)方法,实例化Bean之后调用");
return bean;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
System.out.println("【08】✿这是 InstantiationAwareBeanPostProcessorAdapter调用postProcess(After)Instantiation(bean,beanName)(boolean)方法");
return true;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("【16】✿这是InstantiationAwareBeanPostProcessorAdapter调用postProcess(Before)Initialization(bean,beanName)(Object)方法");
return bean;
}
// 接口方法、设置某个属性时调用
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
System.out.println("【09】✿这是InstantiationAwareBeanPostProcessorAdapter调用postProcessPropertyValues方法,设置某个属性时调用");
return pvs;
}
}
public class BeanLifeCycleApp {
public static void main(String[] args) {
System.out.println("【00】现在开始初始化容器(代码没有执行)");
ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("【21】容器初始化成功");
// 得到Preson ,并使用
Person person = factory.getBean("person", Person.class);
System.out.println("【22】调用对象Person " + person);
System.out.println("【23】现在开始关闭容器!");
factory.registerShutdownHook();
}
}
<bean id="person" class="com.junzitaotao.ioc.lifecycle.domain.Person"
init-method="myInit" destroy-method="myDestory" scope="singleton">
<property name="name" value="张三"/>
<property name="address" value="广州"/>
<property name="phone" value="15900000000"/>
</bean>
<bean id="beanPostProcessor" class="com.junzitaotao.ioc.lifecycle.beanprocessor.MyBeanPostProcessor">
</bean>
<bean id="instantiationAwareBeanPostProcessor"
class="com.junzitaotao.ioc.lifecycle.beanprocessor.MyInstantiationAwareBeanPostProcessor">
</bean>
<bean id="beanFactoryPostProcessor" class="com.junzitaotao.ioc.lifecycle.beanprocessor.MyBeanFactoryPostProcessor">
</bean>
执行效果
【00】现在开始初始化容器(代码没有执行)
【01】❈这是BeanFactoryPostProcessor实现类构造器!!
【02】❈这是BeanFactoryPostProcessor调用postProcessBeanFactory方法
【03】★这是BeanPostProcessor实现类构造器!!
【04】✿这是InstantiationAwareBeanPostProcessorAdapter实现类构造器!!
【05】✿这是InstantiationAwareBeanPostProcessorAdapter调用postProcess(Before)Instantiation(beanClass,beanName)(Object)方法,实例化Bean之前调用
【06】✿这是InstantiationAwareBeanPostProcessorAdapter调用determineCandidateConstructors方法
【07】调用Person的构造器实例化(没有则调用无参的)【Person构造器】
【08】✿这是 InstantiationAwareBeanPostProcessorAdapter调用postProcess(After)Instantiation(bean,beanName)(boolean)方法
【09】✿这是InstantiationAwareBeanPostProcessorAdapter调用postProcessPropertyValues方法,设置某个属性时调用
【10】注入属性name【反射调用setter注入属性】
【11】注入属性address【反射调用setter注入属性】
【12】注入属性phone【反射调用setter注入属性】
【13】调用BeanNameAware.setBeanName()【BeanNameAware接口】
【14】调用BeanFactoryAware.setBeanFactory()【BeanFactoryAware接口】
【15】★这是BeanPostProcessor接口方法postProcessBeforeInitialization(bean,beanName)(Object)对属性进行更改!
【16】✿这是InstantiationAwareBeanPostProcessorAdapter调用postProcess(Before)Initialization(bean,beanName)(Object)方法
【17】调用InitializingBean.afterPropertiesSet()【InitializingBean接口】
【18】调用<bean>的init-method属性指定的初始化方法【init-method】
【19】★这是 BeanPostProcessor接口方法postProcessAfterInitialization(bean,beanName)(Object)对属性进行更改!
【20】✿这是InstantiationAwareBeanPostProcessorAdapter调用postProcess(After)Initialization(bean,beanName)(Object)方法,实例化Bean之后调用
【21】容器初始化成功
【22】调用对象Person Person [address=广州, name=张三, phone=110]
【23】现在开始关闭容器!
【24】调用DiposibleBean.destory()【DiposibleBean接口】
【25】调用<bean>的destroy-method属性指定的初始化方法【destroy-method】
Spring-AOP
AOP概述
AOP解决的主要问题:将重复性的功能,独立出来,然后再融合到业务功能中,完成和原来一样的业务操作。这个融入的事情,由框架来做,提高了研发人员的工作效率。
AOP概念
| 概念 | 作用 | 备注 |
|---|---|---|
| Advice | 通知,指重复性的功能 | |
| Joinpoint | 连接点,将通知实施的地方 | 一般指某个方法 |
| Pointcut | 切入点,匹配连接点的规则 | |
| Target | 目标对象 | 一般指实施通知的目标Java对象 |
| Weaving | 织入,指实施通知的过程 | |
| Proxy | 代理,织入的具体实现手段 | Spring使用动态代理 |
| Aspect | 切面 | Aspect = Pointcut + JoinPoint + Advice |
| Advisor | 切面,Spring表示切面的关键词 |
AOP切面类型
切面可以分为三类:一般切面、切点切面和引介切面。其中,引介切面几乎不用,可以忽略。
| 切面类型 | 接口 | 描述 |
|---|---|---|
| 通用切面 | org.springframework.aop.Advisor | Spring AOP 默认的切面类型。 由于 Advisor 接口仅包含一个 Advice(通知)类型的属性,而没有定义 PointCut(切入点),因此它表示一个不带切点的简单切面。 这样的切面会对目标对象(Target)中的所有方法进行拦截并织入增强代码。由于这个切面太过宽泛,因此我们一般不会直接使用。 |
| 切点切面 | org.springframework.aop.PointcutAdvisor | Advisor 的子接口,用来表示带切点的切面,该接口在 Advisor 的基础上还维护了一个 PointCut(切点)类型的属性。 使用它,我们可以通过包名、类名、方法名等信息更加灵活的定义切面中的切入点,提供更具有适用性的切面。 |
| 引介切面 | org.springframework.aop.IntroductionAdvisor | Advisor 的子接口,用来代表引介切面,引介切面是对应引介增强的特殊的切面,它应用于类层面上,所以引介切面适用 ClassFilter 进行定义。 |
AOP通知类型
| 通知类型 | 接口 | 描述 |
|---|---|---|
| 前置通知 | org.springframework.aop.MethodBeforeAdvice | 在目标方法执行前实施增强。 |
| 后置通知 | org.springframework.aop.AfterAdvice | 在目标方法执行后实施增强。 |
| 后置返回通知 | org.springframework.aop.AfterReturningAdvice | 在目标方法执行完成,并返回一个返回值后实施增强。 |
| 环绕通知 | org.aopalliance.intercept.MethodInterceptor | 在目标方法执行前后实施增强。 |
| 异常通知 | org.springframework.aop.ThrowsAdvice | 在方法抛出异常后实施增强。 |
| 引入通知 | org.springframework.aop.IntroductionInterceptor | 在目标类中添加一些新的方法和属性。 |
AOP代理(Proxy)类型
| 代理技术 | 描述 |
|---|---|
| JDK 动态代理 | Spring AOP 默认的动态代理方式,若目标对象实现了若干接口,Spring 使用 JDK 的 java.lang.reflect.Proxy 类进行代理。 |
| CGLIB 动态代理 | 若目标对象没有实现任何接口,Spring 则使用 CGLIB 库生成目标对象的子类,以实现对目标对象的代理。 |
形象描述AOP
Aspect = Pointcut + JoinPoint + Advice
| 概念 | 描述 | 备注 |
|---|---|---|
| 增强(Advice) | 传播基因 | 有婚前传播,婚后传播,婚前婚后都传播,有离婚后传播。 |
| 切入点(PointCut) | 审美观 | 就是说,你喜欢什么样的女人 |
| 连接点(JoinPoint) | 女人 | |
| 切面(Aspect) | 找个喜欢的女人,传播基因 | 符合审美观(Pointcut)的女人(JoinPoint)和传播基因(Advice)构成了切面。 |
AOP使用场景
AOP的使用场景非常多,这里列举的只是常见的几种,供参考。
| 场景 | 案例 |
|---|---|
| 权限 | 通用使用注解标注在方法上,对特定的方法进行权限拦截 |
| 缓存 | Spring Cache,通过@Cache注解,缓存方法返回值 |
| 参数传递 | 例如参数透传整个调用栈,例如ExposeInvocationInterceptor |
| 错误处理 | SpringMVC的@ControllerAdvice和@ExceptionHandler,处理全局异常 |
| 对象懒加载 | IOC容器启动时,对象不调用,可以先不加载,待调用时再进行加载 |
| 日志 | |
| 监控 | |
| DEBUG | |
| 限流 | |
| 重试 | |
| 持久化 | |
| 资源池 | |
| 同步 | |
| 事务 | |
| 测试 | |
| 数据源切换 | |
| Mock | |
| 分布式锁 |
AOP常用开发方式
基于PointcutAdvisor的切面开发
业务功能
public interface UserDao {
public void add();
}
public class UserDaoImpl implements UserDao {
@Dao
@Override
public void add() {
System.out.println("正在执行 UserDao 的 add() 方法……");
}
}
注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Dao {
}
切面
public class UserDaoAdvisor extends AbstractPointcutAdvisor {
private Advice advice;
private Pointcut pointcut;
public UserDaoAdvisor(Advice advice) {
this.advice = advice;
}
@Override
public Pointcut getPointcut() {
return AnnotationMatchingPointcut.forMethodAnnotation(Dao.class);
}
@Override
public Advice getAdvice() {
return advice;
}
}
通知
public class UserDaoInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("环绕增强前********");
//执行被代理对象中的逻辑
Object result = methodInvocation.proceed();
System.out.println("环绕增强后********");
return result;
}
}
配置
@EnableAspectJAutoProxy // 开启AspectJ切面
@Configuration
public class UserConfiguration {
@Bean
public UserDaoAdvisor userDaoAdvisor() {
return new UserDaoAdvisor(new UserDaoInterceptor());
}
@Bean
public UserDao userDao() {
return new UserDaoImpl();
}
}
测试
public class MainApp {
public static void main(String[] args) {
//获取 ApplicationContext 容器
ApplicationContext context = new AnnotationConfigApplicationContext(UserConfiguration.class);
//获取代理对象
UserDao userDao = context.getBean("userDao", UserDao.class);
//调用 UserDao 中的各个方法
userDao.add();
}
}
基于AspectJ的XML切面开发
切面 & 通知
public class UserDaoAspect {
// 前置通知
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("-----------------------前置通知-----------------------");
}
// 后置通知
public void afterReturningAdvice(JoinPoint joinPoint) {
System.out.println("-----------------------后置通知-----------------------");
}
// 环绕通知
public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("-----------------------环绕通知-开始-----------------------");
//执行目标方法
Object result = proceedingJoinPoint.proceed();
System.out.println("-----------------------环绕通知-结束-----------------------");
return result;
}
// 异常通知
public void afterThrowingAdvice(JoinPoint joinPoint, Throwable e) {
System.out.println("-----------------------异常通知-----------------------");
}
// 最终通知
public void afterAdvice() {
System.out.println("-----------------------最终通知-----------------------");
}
}
切面配置
<?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 -->
<bean id="userDao" class="com.junzitaotao.aop.aspect.aspectj.xml.dao.UserDaoImpl"/>
<!-- 切面类Bean -->
<bean id="userDaoAspect" class="com.junzitaotao.aop.aspect.aspectj.xml.aspect.UserDaoAspect"/>
<!-- 切面配置 -->
<aop:config>
<!-- 配置切面 -->
<aop:aspect ref="userDaoAspect">
<!-- 配置切入点 : 对哪些方法进行增强处理/通知 -->
<aop:pointcut id="userDaoPointcut"
expression="execution(* com.junzitaotao.aop.aspect.aspectj.xml..*.*(..))"/>
<!-- 前置通知 -->
<aop:before method="beforeAdvice" pointcut-ref="userDaoPointcut"/>
<!--
后置通知,在方法执行之后执行,就可以获得返回值。
returning属性 : 用于设置后置通知的第二个参数的名称,类型是Object
-->
<aop:after-returning method="afterReturningAdvice" pointcut-ref="userDaoPointcut" returning="returnVal"/>
<!-- 环绕通知 -->
<aop:around method="aroundAdvice" pointcut-ref="userDaoPointcut"/>
<!--
异常通知 : 抛出异常,用于处理程序发生异常。如果没有异常,则不会执行该增强
throwing属性 : 用于设置"通知的第二个参数"的名称,类型是Throwable
-->
<aop:after-throwing method="afterThrowingAdvice" pointcut-ref="userDaoPointcut" throwing="e"/>
<!-- 最终通知 : 无论程序发生任何事情,都将执行 -->
<aop:after method="afterAdvice" pointcut-ref="userDaoPointcut"/>
</aop:aspect>
</aop:config>
</beans>
测试
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("============================= start =========================================");
// 使用无参构造方法,创建对象
UserDao userDao = context.getBean("userDao", UserDao.class);
userDao.add();
System.out.println("============================= end =========================================");
}
}
基于AspectJ的Annotation切面开发
基于XML的声明式 ApectJ 开发,存在着一些缺点,那就是 要在xxx.xml配置文件中配置大量的信息。
AspectJ框架为AOP的实现提供了一套注解,可以实现同样的效果,更符合主流的开发方式。
| 注解名称 | 描述 |
|---|---|
| @Aspect | 定义一个切面 |
| @Pointcut | 定义切入点表达式 |
| @Before | 定义 前置通知,相当于BeforeAdvice |
| @AfterReturning | 定义 后置通知,相当于AfteRturingAdvice |
| @Around | 定义 环绕通知,相当于Methodlnterceptor |
| @AferThrowing | 定义 异常通知,来处理程序中未处理的异常,相当于ThrowAdvice |
| @After | 定义 最终final通知,不管是否异常,该通知都会执行 |
如果在同一个连接点有多个通知需要执行,那么在同一切面中, 目标方法之前的前置通知和环绕通知的执行顺序是未知的,目标方法之后的后置通知和环绕通知的执行顺序也是未知的。
切面 & 通知
// 定义切面
@Aspect
@Component
public class UserDaoAspect {
// 定义切入点表达式
@Pointcut("execution(* com.junzitaotao.aop.aspect.aspectj.annotation..*.*(..))")
private void userDaoPointcut() {
}
// 前置通知
@Before(value = "userDaoPointcut()")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("-----------------------前置通知-----------------------");
}
// 后置通知
@AfterReturning(value = "userDaoPointcut()")
public void afterReturningAdvice(JoinPoint joinPoint) {
System.out.println("-----------------------后置通知-----------------------");
}
// 环绕通知
@Around(value = "userDaoPointcut()")
public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("-----------------------环绕通知-开始-----------------------");
//执行目标方法
Object result = proceedingJoinPoint.proceed();
System.out.println("-----------------------环绕通知-结束-----------------------");
return result;
}
// 异常通知
@AfterThrowing(value = "userDaoPointcut()", throwing = "e")
public void afterThrowingAdvice(JoinPoint joinPoint, Throwable e) {
System.out.println("-----------------------异常通知-----------------------");
}
// 最终通知
@After(value = "userDaoPointcut()")
public void afterAdvice() {
System.out.println("-----------------------最终通知-----------------------");
}
}
通过注解开启切面
@Configuration
// 启动基于注解的AspectJ
@EnableAspectJAutoProxy
// 指定需要扫描的包,加载bean对象
@ComponentScan(basePackages = "com.junzitaotao.aop.aspect.aspectj.annotation")
public class UserConfiguration {
}
测试
public class MainApp {
public static void main(String[] args) {
//获取 ApplicationContext 容器
ApplicationContext context = new AnnotationConfigApplicationContext(UserConfiguration.class);
System.out.println("============================= start =========================================");
// 使用无参构造方法,创建对象
UserDao userDao = context.getBean("userDao", UserDao.class);
userDao.add();
System.out.println("============================= end =========================================");
}
}
小结
| 方式 | 特点 | 使用场景 |
|---|---|---|
| PointcutAdvisor | 灵活,扩展性很强,适配各种各样的场景,但需要对底层有深入了解,才能写出高质量的切面功能 | 一般用于业务组件、技术组件 |
| Aspectj-XML | 配置比较繁琐,可以集中在一个配置文件 | 常见业务代码 |
| Aspectj-注解 | 注解和通知方法一起,相对比较灵活。更灵活的注解玩法 | 常见业务代码 |
Spring-事务
Spring-Transaction是什么?
JDBC的标准事务的样板代码
Connection conn = null;
try {
// 加载并注册 JDBC 驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
// 建立数据库连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "username", "password");
// 关闭自动提交
conn.setAutoCommit(false);
// 创建 Statement 对象执行 SQL 语句
Statement statement = conn.createStatement();
// 【业务逻辑】执行业务SQL
statement.executeUpdate("INSERT INTO mytable (column1) VALUES ('value1')");
statement.executeUpdate("UPDATE mytable SET column1 = 'value2' WHERE column1 = 'value1'");
// 提交事务
conn.commit();
} catch (ClassNotFoundException | SQLException e) {
try {
// 出现异常时回滚事务
if (conn != null) {
conn.rollback();
System.out.println("事务回滚完成!");
}
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
} finally {
try {
// 关闭连接和 Statement
if (conn != null) {
conn.setAutoCommit(true);
conn.close();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
}
JDBC事务,大部分代码都是固定的,这非常符合AOP的设计理念,将事务的操作,以Spring擅长的设计风格,进行抽象和封装,让开发者很容易用上事务。
所以,Spring-Transaction,就是以AOP为核心,Template为辅助,为开发者提供轻量级的事务解决方案。
事务常用开发方式
编程式事务
@SpringBootApplication
@MapperScan("com.junzitaotao.transaction.api.dao")
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Resource
private UserService userService;
/**
* 编程式事务有多种方式,transactionTemplate是最常用的一种,也是Spring官方推荐的编程式事务
*/
@Resource
private TransactionTemplate transactionTemplate;
@Override
public void run(String... args) {
System.out.println("======================== start =============================");
transactionTemplate.execute(transactionStatus -> {
long result1 = userService.create(UserDTO.of().setId(111L).setName("Jack"));
System.out.println("保存第一条记录,result=" + result1);
// 制造异常,最终结果就是,两条数据,都未保存成功
Exceptions.mock();
long result2 = userService.create(UserDTO.of().setId(112L).setName("Tom"));
System.out.println("保存第一条记录,result=" + result2);
return true;
});
System.out.println("======================== end =============================");
}
}
XML声明式事务
XML配置事务
<!-- 配置数据源 -->
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource">
<!-- 数据库连接配置 -->
<property name="url" value="${spring.datasource.url}"/>
<property name="driverClassName" value="${spring.datasource.driver-class-name}"/>
<property name="username" value="${spring.datasource.username}"/>
<property name="password" value="${spring.datasource.password}"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务切面 -->
<aop:config>
<aop:pointcut id="transactionalPointcut" expression="execution(* com.junzitaotao.transaction..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="transactionalPointcut"/>
</aop:config>
模拟事务异常
@Service("userService")
public class UserServiceImpl implements UserService {
@Resource
private UserRepository userRepository;
@Override
public Long createTwo() {
userRepository.create(UserDTO.of().setId(111L).setName("Jack"));
// 制造异常,最终结果就是,两条数据,都未保存成功
Exceptions.mock();
userRepository.create(UserDTO.of().setId(112L).setName("Tom"));
return null;
}
}
注解声明式事务【推荐】
/**
* 仅声明@Transactional注解,即可使用Spring事务
* <p>
* 注意:
* 1、@Transactional,可以声明在方法、类上
* 2、一般不建议声明在类上,在复杂的系统,复杂度不易管理
*/
@Override
@Transactional(rollbackFor = Throwable.class)
public Long createTwo() {
userRepository.create(UserDTO.of().setId(111L).setName("Jack"));
// 制造异常,最终结果就是,两条数据,都未保存成功
Exceptions.mock();
userRepository.create(UserDTO.of().setId(112L).setName("Tom"));
return null;
}