从0到1学SpringCore

88 阅读16分钟

junzitaotao_weixin_white.png

IOC是Spring的灵魂。它改变了大家的编程习惯,以至于大家认为,Java编程如此简单。

Spring-IOC

IOC和DI是什么?

IOC(Inversion of Control),字面意思是控制反转。这是一种重要的架构设计原则。

一个功能实现,需要三个步骤:创建对象(多个)、组合对象、执行功能。前两个步骤,既繁琐,也不涉及功能逻辑。如果有一个中介,帮助研发人员执行前两个步骤,能显著提高研发的效率。

Spring基于IOC设计原则,创造了IOC容器。在容器的管理下,对象之间的依赖关系,变得简单而又清晰。

在互联网公司,业务和系统非常复杂,一个系统的对象多达万千,单靠人力的手动管理,几乎不可能,效率非常低下。

DI(Dependency Injection),即依赖注入。DI干的是第二件事,组合对象。

创建IOC容器

链接:gitee.com/junzitaotao…

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配置方式

链接:gitee.com/junzitaotao…

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配置方式

链接:gitee.com/junzitaotao…

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();
    }
}

注解配置方式

链接:gitee.com/junzitaotao…

@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配置方式

链接:gitee.com/junzitaotao…

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配置方式

链接:gitee.com/junzitaotao…

@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 =========================================");
    }

}

注解配置方式

链接:gitee.com/junzitaotao…

@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 =========================================");
    }
}

对象的生命周期管理

链接:gitee.com/junzitaotao…

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.AdvisorSpring AOP 默认的切面类型。 由于 Advisor 接口仅包含一个 Advice(通知)类型的属性,而没有定义 PointCut(切入点),因此它表示一个不带切点的简单切面。 这样的切面会对目标对象(Target)中的所有方法进行拦截并织入增强代码。由于这个切面太过宽泛,因此我们一般不会直接使用。
切点切面org.springframework.aop.PointcutAdvisorAdvisor 的子接口,用来表示带切点的切面,该接口在 Advisor 的基础上还维护了一个 PointCut(切点)类型的属性。 使用它,我们可以通过包名、类名、方法名等信息更加灵活的定义切面中的切入点,提供更具有适用性的切面。
引介切面org.springframework.aop.IntroductionAdvisorAdvisor 的子接口,用来代表引介切面,引介切面是对应引介增强的特殊的切面,它应用于类层面上,所以引介切面适用 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的切面开发

链接:gitee.com/junzitaotao…

业务功能

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切面开发

链接:gitee.com/junzitaotao…

切面 & 通知

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通知,不管是否异常,该通知都会执行

如果在同一个连接点有多个通知需要执行,那么在同一切面中, 目标方法之前的前置通知和环绕通知的执行顺序是未知的,目标方法之后的后置通知和环绕通知的执行顺序也是未知的。

链接:gitee.com/junzitaotao…

切面 & 通知

// 定义切面
@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为辅助,为开发者提供轻量级的事务解决方案。

事务常用开发方式

编程式事务

链接:gitee.com/junzitaotao…

@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声明式事务

链接:gitee.com/junzitaotao…

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;
    }
}

注解声明式事务【推荐】

链接:gitee.com/junzitaotao…

/**
 * 仅声明@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;
}