Spring框架

Spring概述

Spring是一个分层的java SE/EE 全站轻量级框架,它以IoC(Inversion of Control 控制反转)和AOP(Aspect Oriented Programming面向切面编程)为内核,使用基本的javaBean来完成以前只能由EJB(Enterprise Java Beans,Java企业Bean)完成的工作。

Spring 的下载及目录结构

Spring 开发所需的Jar包分为两部分,具体如下。这里以Spring 4.3.6为例

  1. Spring框架包

下载地址:http://repo.spring.io/simple/libs-release-local/org/springframework/spring/4.3.6.RELEASE/
文件名称:spring-framework-4.3.6.RELEASE-dist

解压后的文件目录
在这里插入图片描述

  • docs文件夹中包含Spring的API文档和开发规范;
  • libs文件夹中包含开发需要的JAR包和源码;
  • schema文件夹中包含开发所需要的schema文件,这些文件定义了Spring相关配置文件的约束

在libs目录中,有四个Spring的基础包,分别对应Spring核心容器的四个模块:

  • spring-core-4.3.6.RELEASE.jar: 包含Spring框架基本的核心工具类,Spring 其他组件都要用到这个包里的类,是其他组件的基本核心。
  • spring-beans-4.3.6.RELEASE.jar: 所有应用都要用到的JAR包,它包含访问配置文件、创建和管理Bean以及进行Inversion of Control(loC)或者 Dependency Injection(DI)操作相关的所有类。
  • spring-context-4.3.6.RELEASE.jar: Spring 提供了在基础loC功能上的扩展服务,还提供了许多企业级服务的支持,如邮件服务、任务调度、 JNDI 定位、 EJB 集咸、远程访问、缓存以及各种视图层框架的封装等。
  • spring-expression-4.3.6.RELEASE.jar: 定义了Spring的表达式语言。
  1. 第三方依赖包

Spring核心容器还需要依赖commons.logging的jar包,地址:http://commons.apache.org/proper/commons-loggingdownload_logging.cgi

Spring核心容器

Spring框架主要功能是通过其核心容器来实现,Spring框架提供了两种核心容器:BeanFactory和ApplicationContext。

BeanFactory

BeanFactory 由 org.springframework.beans.facytory.BeanFactory 接口定义,是基础类型的 loC 容器.BeanFactory是一个管理Bean工厂,主要负责初始化各种Bean和调用它们的生命周期方法。
BeanFactory提供的几个实现类中,最长常用的是org.springframework.beansfactory.xml.XmIBeanFactory,该类会根据xml的配置文件中的定义来装配Bean.

BeanFactory beansFactory=new XmlBeanFactory(new FileSystemResource("F:/applicationContext.xml"));

ApplicationContext

ApplicationContext是BeanFactory的子接口,是另一种Spring核心容器。它由 org.springframework.context.ApplicationContext 接口定义,不仅包含了BeanFactory 的所有功能,还添加了对国际化、资源访问、事件传播等方面的支持。
创建ApplicationContext接口实例,通常有2中方法:

  1. 通过ClassPathXmlApplicationContext创建
String xmlPath= "applicationContext.xml";
ClassPathXmlApplicationContext applicationContext =new ClassPathXmlApplicationContext(xmlPath);
  1. 通过FileSystemXmlApplicationContext创建
ApplicationContext applicationContext=new FileSystemXmlApplicationContext(String configLocation);

与 ClassPathXmlApplicationContext 有所不同的是,FileSystemXmlApplicationContext 不再从类路径 中读取配置文件,而是通过参数指定配置文件的位置,例如 “0:/workspaces/appIicationContext.xm 1”。

  1. web中实例化ApplicationContext
    在web项目中,ApplicationContext容器的实例化工作交由Web服务器来完成。Web服务器通过ContextLoaderListener方式来实例化,在web.xml中添加代码:
<!… 指定 Spring 配置文件的位置,多个配置文件时,以逗号分隔一〉
<context-param>
  <param-name>contextConfigLocation</param-name>
<!一 Spring 将加载 spring 目录下的 applicationContext .xml 文件一 
  <param-value>
    classpath:spring/applicationContext.xml
  </param-value>
<context-param>
    <!一指定以 ContextLoaderListener 方式启动 Spring 容器 一〉
<listener>
  <listener-class>
    org.springframework.web.context.ContextLoaderListener
  </listener-class>
</listener>

创建 Spring 容器后,就可以获取 Spring 容器中的 Bean。Spring 获取 Bean 的实例通常采用以下两种方法。

  • Object getBean(String name): 根据容器中 Bean 的id或name来获取指定的 Bean ,获取之后需要进行强制类型转换。
  • T getBean(Class requiredType) : 根据类的类型来获取Bean 的实例。由于此方法为泛型方法,因此在获取 Bean 之后不需要进行强制类型转换。

Spring入门案例

  1. 导入四个基础包和日志包
  2. cn.edu.sicau.ioc包下创建UserDao接口类
public interface UserDao {

    public void say();
}
  1. 实现类 UserDaoImpl
public class UserDaoImpl implements UserDao{

    @Override
    public void say() {
        System.out.println("userDao say hello World!");
    }
}
  1. 在ioc包下创建applicationContext.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

    <!--配置目标类-->
    <bean id="userDao" class="cn.edu.sicau.ioc.UserDaoImpl"/>
</beans>
  1. 在ioc包下创建测试类IoCTest
    @Test
    public void funcIoc1(){
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("cn/edu/sicau/ioc/applicationContext.xml");
        UserDao userDao = (UserDao) applicationContext.getBean("userDao");
        userDao.say();
    }

运行结果:
在这里插入图片描述

依赖注入

依赖注入的概念

依赖注入和反向控制的含义相同,是从两个不同角度描述同一个概念。
当一个Java对象(调用者)需要调用另一个Java对象(被调用者)时,在传统模式下,调用者通常采用“new 被调用者”的代码来创建对象,

new
调用者
被调用者

流程图语法(https://mermaidjs.github.io/flowchart.html)
使用Spring框架后,对象的实例不再由调用者创建,而是由Spring容器来创建。控制权由应用程序转移到了Spring容器,控制权发生了反转,这就是Spring的控制反转。
从Spring容器的角度来看,Spring容器负责将被依赖对象赋值给调用者的成员变量,这相当于为调用者注入了它的依赖实例,这就是Spring的依赖注入。

容器注入被调用者实例
被调用者
调用者

依赖注入的两种实现方式

属性setter方法注入:

IoC容器使用setter方法注入被依赖的实例

  1. 在Spring入门实例的基础上在ioc包下创建UserService接口类
public interface UserService {
    public void say();
}
  1. 在ioc包下创建UserServiceImpl实现类
public class UserServiceImpl implements UserService{

    private UserDao UserDao;

    //setter方法
    public void setUserDao(UserDao UserDao) {
        this.UserDao = UserDao;
    }

    @Override
    public void say() {
        this.UserDao.say();
        System.out.println("UserService say Hello World!");
    }
}

  1. 在配置文件中创建一个id为userService的Bean,该Bean用于实例化UserServiceImpl,并将UserDao的实例注入到userService中。
<bean id="userService" class="cn.edu.sicau.ioc.UserServiceImpl">
    <property name="userDao" ref="userDao"/>
</bean>
  1. 测试类
@Test
    public void funcIoc2(){
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("cn/edu/sicau/ioc/applicationContext.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.say();
    }

运行结果:
在这里插入图片描述

构造方法注入

IoC使用构造方法注入被依赖的实例
对应的在配置文件中

  <!--<property name="userDao" ref="userDao"/>-->
  <constructor-arg name="UserDao" ref="userDao"/>

Spring中的Bean

Bean的配置

Spring容器支持XML和Properties两种格式的配置文件,其中xml配置最常用。XML配置文件中根元素是,包含多个子元素,每个子元素定义了一个Bean。的常用属性及子元素如下:
[外链图片转存失败(img-eIgcoQkR-1564703784760)(assets/spring-425e31a0.png)]

Bean的实例化

实例化Bean有三种方式,分别:构造器实例化、静态工厂实例化、实例工厂实例化。

构造器实例化

构造器实例化指通过Bean对应类中的默认无参构造函数方法来实例化Bean。
案例

  1. Bean1
public class Bean1(){

}
  1. 配置文件
<?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-4.3.xsd">
    <!--配置目标类-->
    <bean id="bean1" class="cn.edu.sicau.ioc.Bean1"/>
</beans>

这里测试类就省略

静态工厂方式实例化

通过创建一个静态工厂的方法来创建Bean实例,其中Bean配置的class属性不再是Bean实例的实现类,而是静态工厂类,同时使用factory-method属性来指定所创建的静态工厂方法。

  1. 创建Bean2类,与上例中的Bean1类一样
    2.创建静态工厂类 MyStaticFactory()来返回Bean2
public class MyStaticFactory {

    public static Bean2 createBean(){
        return new Bean2();
    }
}
  1. 创建bean2.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

    <bean id="bean2" class="cn.edu.sicau.instance.static_factory.MyStaticFactory"
    factory-method="createBean"/>
</beans>
  1. 测试类
    @Test
    public void func(){

        ClassPathXmlApplicationContext applicationContext=new
                ClassPathXmlApplicationContext("cn/edu/sicau/instance/static_factory/Bean2.xml");

        Bean2 bean2 = (Bean2) applicationContext.getBean("bean2");

        System.out.println(bean2);
    }

实例工厂方式实例化

在配置文件中实例化Bean,不使用class属性直接指向实例化类,而是通过factory-bean属性指向配置的实例工厂,然后使用factory-method属性确定使用工厂中的那个方法。
实例

  1. 创建Bean3,与上例中的Bean1一样
  2. 创建实例工厂类Factory
public class Factory {
  //创建Bean3实例的方法
    public Bean3 createBean(){
        return new Bean3();
    }
}
  1. 配置文件
<?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-4.3.xsd">
    <!-- 配置工厂 -->
    <bean id="myFactory" class="cn.edu.sicau.instance.factory.Factory"/>
    <!--使用factory-bean 指向实例化工厂
        使用factory-method确定使用工厂中的那个方法-->
    <bean id="bean3" factory-bean="myFactory" factory-method="createBean"/>
</beans>

Bean的装配方式

Spring容器常用三种Bean的装配方式,如基于XML的装配、基于注解装配、自动装配(其中基于注解最常用)。

基于XML的装配

Spring提供了2种XML的装配方式:设值注入(Setter Injection)和构造注入(Constructor Injection).
在实例化Bean的过程中,Spring首先会调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式调用setter方法来注入属性值。所以设值注入要求一个Bean必须满足一下两点:

  • Bean类必须提供一个默认的无参构造方法
  • Bean类必须为需要注入属性提供对应的setter方法
    使用设值注入时,配置文件中元素使用子元素来为每个属性注入值;构造注入时,元素使用子元素来定义构造方法的参数,可以使用value或子元素来设置参数值。

实例:两种方式同时演示

  1. 创建User类
public class User {
    private String username;
    private Integer password;

    private List<String> list;

    /*
      无参构造函数
      设值注入
    */
    public User(){
        super();
    }
    /*
    有参构造函数
    构造注入
    */
    public User(String username, Integer password, List<String> list) {
        this.username = username;
        this.password = password;
        this.list = list;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(Integer password) {
        this.password = password;
    }

    public void setList(List<String> list) {
        this.list = list;
    }
}
  1. 配置文件
<?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-4.3.xsd">

    <!--构造注入-->
    <bean id="user1" class="cn.edu.sicau.ioc.User">
        <constructor-arg index="0" value="张三"/>
        <constructor-arg index="1" value="123"/>
        <constructor-arg index="2">
            <list>
                <value>"constructor1"</value>
                <value>"constructor2"</value>
                <value>"constructor3"</value>
            </list>
        </constructor-arg>
    </bean>

    <!--设值注入-->
    <bean id="user2" class="cn.edu.sicau.ioc.User">
        <property name="username" value="李四"/>
        <property name="password" value="456"/>
        <property name="list" >
            <list>
                <value>"setListValue1"</value>
                <value>"setListValue2"</value>
            </list>
        </property>
    </bean>

</beans>
  1. 测试类
    @Test
    public void funcIoc3(){
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("cn/edu/sicau/ioc/UserBean.xml");
        User user1 = (User) applicationContext.getBean("user1");

        User user2 = (User) applicationContext.getBean("user2");

        System.out.println(user1);
        System.out.println(user2);
    }

测试结果
在这里插入图片描述

基于Annotation的装配

常用注解

  • @Component:用来描述Spring中的Bean,可以作用在任何层次,仅仅标识一个组件(Bean)。
  • @Repository: 用于将数据访问层(DAO层)的类标识为Spring中的Bean,其功能与@Component相同。
  • @Service:用于业务层(Service层),用于将业务层的类标识为Bean,其功能与@Component相同。
  • @Controller:用于控制层的类标识为Bean,其功能与@Component相同。
  • @Autowired: 用于对 Bean 的属性变量、属性的 setter 方法及构造方法进行标注,配合对应的注解处理器完成 Bean 的自动配置工作。 默认按照 Bean 的类型进行装配。
    • @Resource: 其作用与@Autowired一样。其区别在于@Autowired 默认按照Bean类型装配,而@Resource 默认按照 Bean 实例名称进行装配。@Resource 中有两个重要属性: name和type。Spring 将 name 属性解析为 Bean 实例名称,type 属性解析为Bean实例类型。如果指定name属性,则按实例名称进行装配;如果指定type属性,则按Bean类型进行装配;如
    果都不指定,则先按 Bean 实例名称装配,如果不能匹配,再按照Bean类型进行装自己;如果都无法匹配,则抛出NoSuchBeanDefinitionException 异常 。
    • @Qualifier: 与@Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为接 Bean
    的实例名称装配, Bean 的实例名称由 @Qualifier 注解的参数指定 。

实例
Spring 4.0 以上版本使用上面的代码对指定包中的注解进行扫描前,需要先向项目中导入 Spring AOP 的 JAR 包 spring-aop-4.3.6.RELEASE.jar,否则程序在运行时会报出 “java.lang.NoClassDefFound Error: orglspringframeworklaop/TargetSource” 错误。

  1. 在annotation包下创建DAO层接口:UserDao
public interface UserDao {
    void save();
}
  1. 在annotation包下创建DAO层实现类:UserDao实现类UserDaoImpl
@Repository("userDao")
public class UserDaoImpl implements UserDao{
    @Override
    public void save() {
        System.out.println("userDao ……save……");
    }
}

使用 @Repository 注解将 UserDaolmpl 类标识为 Spring 中的 Bean,其写法相当于配置文件中 的编写

  1. 在annotation包下创建业务层接口:userService
public interface UserService {
    void save();
}
  1. 在annotation包下创建业务层实现类:UserServiceImpl
//标识Bean
@Service("userService")
public class UserServiceImpl implements UserService{

    //这相当于配置文件中 <property name="userDao" ref="userDao"/> 的写法
    @Resource(name = "userDao")
    private UserDao userDao;

    @Override
    public void save() {
        userDao.save();
        System.out.println("userService …… save ……");
    }
}

  1. 在annotation包下控制层创建UserController
//标识Bean
@Controller("userController")
public class UserController {

    //<property name="userService" ref="userService" /
    @Resource(name = "userService")
    private UserService userService;

    public void save(){
        userService.save();

        System.out.println("userController …… save ……");
    }
}
  1. 在annotation包下创建配置文件
<?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
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <!--使用context命名空间,在配置文件中开启注解处理器-->
    <!--<context:annotation-config/>-->

    <!--定义3个bean-->
    <!--<bean id="userDao" class="cn.edu.sicau.annotation.UserDaoImpl"/>
    <bean id="userService" class="cn.edu.sicau.annotation.UserServiceImpl"/>
    <bean id="userController" class="cn.edu.sicau.annotation.UserController"/>-->


    <context:component-scan base-package="cn.edu.sicau.annotation"/>
</beans>

配置文件中注释的部分和<context:component-scan base-package=“cn.edu.sicau.annotation”/>起到同样的作用。

  1. 测试方法
    @Test
    public void func(){
        ClassPathXmlApplicationContext applicationContext=new
                ClassPathXmlApplicationContext("cn/edu/sicau/annotation/bean4.xml");

        UserController userController = (UserController) applicationContext.getBean("userController");
        userController.save();
    }

自动装配

Spring 的元素中包含一个autowire属性,我们可以通过设置autowire 的属性值来自动装配Bean。所谓自动装配,就是将一个 Bean 自动地注入到其他 Bean的Property中。autowire属性有5个值,如下:
[外链图片转存失败(img-sQlDgBJD-1564703784765)(assets/spring-f2a9bd76.png)]

实例

  1. 修改上例中的UserServiceImpl和UserController,增加类属性的setter方法。
  2. 修改配置文件
<?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
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <bean id="userDao" class="cn.edu.sicau.annotation.UserDaoImpl"/>
    <bean id="userService" class="cn.edu.sicau.annotation.UserServiceImpl" autowire="byName"/>
    <bean id="userController" class="cn.edu.sicau.annotation.UserController" autowire="byName"/>
</beans>

Spring AOP

Spring简介

AOP采用横向抽取机制,将分散在各个方法中的重复代码抽取出来,在编译或运行时,再将这些代码应用到需要执行的地方。这里抽取出来的功能代码可以理解是一个个切面,如图:
[外链图片转存失败(img-7dtv2tIP-1564703784767)(assets/spring-6249cd84.png)]

AOP术语

  • Aspect (切面):在实际应用中,切面通常是指封装的用于横向插入系统功能(如事务、日志等)的类。
  • Joinpoint (连接点):在程序执行过程中的某个阶段点,它实际上是对象的一个操作,例
    如方法的调用或异常的抛出 。 在 Spring AOP 中,连接点就是指方法的调用
  • Pointcut (切入点):是指切面与程序流程的交叉点,即那些需要处理的连接点
  • Advice( 通知/增强处理): AOP 框架在特定的切入点执行的增强处理,即在定义好的切入点处所要执行的程序代码 。 可以将其理解为切面类中的方法。
  • Target Object (目标对象):是指所有被通知的对象,也称为被增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象就是一个被代理对象。
  • Proxy (代理):将通知应用到目标对象之后,被动态创建的对象 。
  • Weaving (织入):将切面代码插入到目标对象上,从而生成代理对象的过程。

[外链图片转存失败(img-YFtx3lcg-1564703784769)(assets/spring-bc3a56e9.png)]

动态代理

AOP中的代理就是由AOP框架动态生成一个对象,该对象可以作为目标对象使用。Spring中的动态代理,可以是JDK动态代理,也可以是CGLIB代理。

JDK动态代理

JDK 动态代理是通过 java.lang. reflect. Proxy 类来实现的,我们可以调用 Proxy 类的newProxylnstanceO方法来创建代理对象。
实例

  1. 创建UserDao,添加增删方法
public interface UserDao {
    void addUser();
    void deleteUser();
}
  1. 创建实现类UserDaoImpl
public class UserDaoImpl implements UserDao{
    @Override
    public void addUser() {
        System.out.println("增加用户");
    }
    @Override
    public void deleteUser() {
        System.out.println("删除用户");
    }
}
  1. 创建切面类
public class MyAspect {

    //前置增强方法
    public void check_permission(){
        System.out.println("检查权限");
    }
    //后置增强方法
    public void log(){
        System.out.println("打印日志");
    }
}
  1. 创建代理类,需要实现InvocationHandler接口
public class JdkProxy implements InvocationHandler {

    //申明目标接口类
    private UserDao userDao;

    public Object createProxy(UserDao userDao){

        this.userDao=userDao;

        //1. 类加载器
        ClassLoader classLoader = JdkProxy.class.getClassLoader();
        //2. 被代理对象实现的所有接口
        Class[] clazz = userDao.getClass().getInterfaces();

        //3. 使用代理类进行增强
        return Proxy.newProxyInstance(classLoader,clazz,this);
    }

    /**
     * 所有目标代理类的方法调用,都会交由invoke来处理
     * @param proxy 被代理后的对象
     * @param method 将要被执行的方法信息
     * @param args 执行方法时需要的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {

        //声明切面
        MyAspect myAspect=new MyAspect();

        //前置增强
        myAspect.check_permission();
        //在目标类上调用方法,并传入参数
        Object invoke = method.invoke(userDao, args);
        //后置增强
        myAspect.log();

        return invoke;
    }
}

  1. 测试类
@Test
    public void func(){

        //创建代理对象
        JdkProxy jdkProxy=new JdkProxy();
        //创建目标对象
        UserDao userDao=new UserDaoImpl();
        //从代理对象中获取增强后的目标对象
        userDao= (UserDao) jdkProxy.createProxy(userDao);
        userDao.addUser();
        userDao.deleteUser();
    }

测试结果
[外链图片转存失败(img-j7DfO9Zq-1564703784772)(assets/spring-99a83983.png)]

CGLIB代理

和JDK动态代理比较,JDK动态代理有一定的局限性(动态代理的对象必须实现一个或多个接口),如果对没有实现接口的类进行代理,可以使用CGLIB代理。
实例

  1. 目标类 UserDao
public class UserDao {

    public void addUser() {
        System.out.println("增加用户");
    }

    public void deleteUser() {
        System.out.println("删除用户");
    }
}
  1. 代理类
public class CGLibProxy implements MethodInterceptor {

    //代理方法
    public Object createProxy(Object target){

        //创建动态增强类对象
        Enhancer enhancer=new Enhancer();

        //设置代理对象的父类
        enhancer.setSuperclass(target.getClass());

        //增加回调函数
        enhancer.setCallback(this);

        //返回创建的代理类
        return enhancer.create();
    }

    /** proxy CGlib 根据指定父类生成的代理对象
        * method 拦截的方法
        * args 拦截方法的参数数组
        * methodProxy 方法的代理对象,用于执行父类的方法
        */
    @Override
    public Object intercept(Object o, Method method, Object[] objects,
                            MethodProxy methodProxy) throws Throwable {
        //创建界面对象
        MyAspect myAspect=new MyAspect();

        //前置增强
        myAspect.check_permission();

        //目标方法执行
        Object invoke = methodProxy.invokeSuper(o, objects);
        //后置增强
        myAspect.log();
        return invoke;
    }
}

测试类省略

基于代理类的AOP实现

Spring通知类型

  • org.aopalliance.intercept.MethodInterceptor (环绕通知)
    在目标方法执行前后实施增强,可以应用于日志、事务管理等功能 。
  • org.springframework.aop.MethodBeforeAdvice (前置通知)
    在目标方法执行前实施增强,可以应用于权限管理等功能。
  • org.springframework.aop.AfterReturningAdvice (后置通知)
    在目标方法执行后实施增强,可以应用于关闭流、上传文件、删除临时文件等功能。
  • org.springframework.aop.ThrowsAdvice (异常通知)
    在方法抛出异常后实施增强,可以应用于处理异常记录曰志等功能 。
  • org.springframework.aop.IntroductionInterceptor (引介通知)
    在目标类中添加一些新的方法和属性,可以应用于修改老版本程序(增强类)。

ProxyFactoryBean

ProxyFactoryBean是FactoryBean接口的实现类,FactoryBean负责实例化一个 Bean,而ProxyFactoryBean 负责为其他Bean创建代理实例。在Spring 中,使用ProxyFactoryBean是创建 AOP 代理的基本方式 。
ProxyFactoryBean常用可配置属性,如下图:
[外链图片转存失败(img-BU5QaX8p-1564703784774)(assets/spring-5a8bd2da.png)]

实例:环绕通知
在核心包的基础上再添加spring-aop-4.3.6.RELEASE.jar 和aopalliance-1.0.jar( http://mvnrepository.com/artifact/aopalliance/aopalliance/1.0" )

  1. 创建切面类MyAspect
public class MyAspect implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        check_permission();
        //执行目标方法
        Object proceed = methodInvocation.proceed();

        log();

        return proceed;
    }

    public void check_permission(){
        System.out.println("检查权限");
    }

    public void log(){
        System.out.println("打印日志");
    }
}

  1. 配置文件
<?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-4.3.xsd">

    <bean id="userDao" class="cn.edu.sicau.jdk.UserDaoImpl"/>

    <bean id="myAspect" class="cn.edu.sicau.factory.MyAspect"/>

    <bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--指定代理实现的接口-->
        <property name="proxyInterfaces" value="cn.edu.sicau.jdk.UserDao"/>
        <!--指定目标对象-->
        <property name="target" ref="userDao"/>
        <!--指定切面环绕通知-->
        <property name="interceptorNames" value="myAspect"/>
        <!--指定代理方式:true 对应cdlib\ false 默认jdk-->
        <property name="proxyTargetClass" value="true"/>
    </bean>

</beans>
  1. 测试类
    @Test
    public void func(){
        String xmlPath="cn/edu/sicau/factory/applicationContext.xml";
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext(xmlPath);
        UserDao userDaoProxy = (UserDao) applicationContext.getBean("userDaoProxy");
        userDaoProxy.addUser();
        userDaoProxy.deleteUser();

    }

测试结果:
在这里插入图片描述

AspectJ开发

Spring的Java配置方式

Spring的Java配置方式是通过 @Configuration 和 @Bean 这两个注解实现的:

  • @Configuration 作用于类上,相当于一个xml配置文件;
  • @Bean 作用于方法上,相当于xml配置中的;
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值