欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring IOC与AOP的简单实现

程序员文章站 2022-07-12 12:56:58
...

IOC

XML方式

  1. 将Spring的核心依赖包全部通过maven导入
<!--    spring-core spring-beans spring-context spring-expression-->
<!--    这几个包的版本必须一致-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
  </dependencies>
  1. 在resources文件夹下创建一个Spring config文件
    Spring IOC与AOP的简单实现
  2. 在java文件夹下面创建一个MVC的基本架构(domain、persistence、service、注意test是用来做单元检测的)
    Spring IOC与AOP的简单实现
    其中Account、AccountDAOImpl、AccountService的代码如下:
public class Account {

    private String userName;

    private String password;

    private int age;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Account(String userName, String password, int age) {
        this.userName = userName;
        this.password = password;
        this.age = age;
    }

    public String getPassword() {
        return password;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }


}
public class AccountDAOImpl implements  AccountDAO {
    @Override
    public void insert() {
        System.out.println("新增用户");
    }

    @Override
    public void deletd() {
        System.out.println("删除用户");
    }

    @Override
    public void update() {
        System.out.println("更新用户");
    }

    @Override
    public void select() {
        System.out.println("查询用户");
    }
}
public class AccountService {

    private AccountDAO accountDAO;

    public AccountService(AccountDAO accountDAO) {
        this.accountDAO = accountDAO;
    }

    public void login(){
        System.out.println("Service的login方法");
        accountDAO.select();
    }

    public AccountDAO getAccountDAO() {
        return accountDAO;
    }

    public void setAccountDAO(AccountDAO accountDAO) {
        this.accountDAO = accountDAO;
    }
}
  1. 根据Account、AccountDAOImpl、AccountService的调用关系(例如Service调用Dao层来实现Account的查询等操作),配置相应的Spring config文件,实现IOC的XML注入方式,注意这里有两种注入方式,一种是构造函数注入,一种是属性注入。
    属性注入:
<!--     传统的配置注入方式 -->
        <bean id="accountDao" class="org.csu.spring.demo.ioc.persistence.AccountDAOImpl"/>
        <bean id="account" class="org.csu.spring.demo.ioc.demain.Account">
<!--            <property name="userName" value="John"/>-->
<!--            <property name="password" value="123456"/>-->
<!--            <property name="age" value="18"/>-->
        </bean>
        <bean id="accountService" class="org.csu.spring.demo.ioc.service.AccountService">
            <property name="accountDAO" ref="accountDao"/>
        </bean>

构造函数注入:

<!--     传统的配置注入方式 -->
        <bean id="accountDao" class="org.csu.spring.demo.ioc.persistence.AccountDAOImpl"/>
        <bean id="account" class="org.csu.spring.demo.ioc.demain.Account">
            <constructor-arg name="userName" value="John"/>
            <constructor-arg name="password" value="666666"/>
            <constructor-arg name="age" value="18"/>
        </bean>
        <bean id="accountService" class="org.csu.spring.demo.ioc.service.AccountService">
            <constructor-arg name="accountDAO" ref="accountDao"/>
        </bean>
  1. 在test文件夹下创建一个demo单元测试单元,进行测试
public class demo {

    private AccountService service;

    @Test
    public  void test(){
        //使用Spring Ioc容器来获取对象
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Account temp = (Account)context.getBean("account") ;
        AccountService service = (AccountService)context.getBean("accountService");
        service.login();
        System.out.println(temp.getUserName() + "," + temp.getPassword() + "," + String.valueOf(temp.getAge()));

    }

}

测试结果如下:
Spring IOC与AOP的简单实现
6. 总结
此方法为Spring IOC实现的基础方法-基于XML配置文件实现的。这样的方法虽然可以实现IOC控制反转,但是有明显的不足。如果有一百个类似的MVC结构的类关系,难道你需要在配置文件配置一百次吗?显然这个是不人性化的,所以接下来介绍注解方式实现IOC。

注解方式

注意:前面的1、2、3步和基于XML配置文件实现IOC的步骤一模一样,这里直接从第四步开始!

  1. 将之前的Spring conf配置文件的bean全部删掉,加上下面这一个语句,注释功能实现的基础就是这个包扫描语句
<!--     开启注解的包扫描-->
    <context:component-scan base-package="org.csu.spring.demo.ioc"/>
  1. 在MVC框架中的各个类里面加上相应的注释

@Component(“name”)用于实体类的声明

@Component("account")
public class Account {
    @Value("Mike")
    private String userName;
    @Value("888888")
    private String password;
    @Value("22")
    private int age;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
    

    public String getPassword() {
        return password;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }


}

@Repository(“name”)用于Dao类的声明

@Repository("accountDao")
public class AccountDAOImpl implements  AccountDAO {
    @Override
    public void insert() {
        System.out.println("新增用户");
    }

    @Override
    public void deletd() {
        System.out.println("删除用户");
    }

    @Override
    public void update() {
        System.out.println("更新用户");
    }

    @Override
    public void select() {
        System.out.println("查询用户");
    }
}

@Service(“name”)用于业务类的声明

@Service("accountService")
public class AccountService {

    //这个注解它会扫描ioc包的内容,根据类型匹配来注入,如果找不到,会注入null值
    @Autowired
    //按名称去制定
    @Resource(name = "accountDao")
    private AccountDAO accountDAO;

    public AccountService(AccountDAO accountDAO) {
        this.accountDAO = accountDAO;
    }

    public void login(){
        System.out.println("Service的login方法");
        accountDAO.select();
    }

    public AccountDAO getAccountDAO() {
        return accountDAO;
    }

    public void setAccountDAO(AccountDAO accountDAO) {
        this.accountDAO = accountDAO;
    }
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(“classpath:applicationContext.xml”)
代表JUnit在相应的环境下进行测试

//用Junit创建环境
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class demo {

    @Autowired
    private AccountService service;


    @Test
    public  void test(){
        service.login();
    }

}
  1. 运行demo1的单元测试,结果如下
    Spring IOC与AOP的简单实现
  2. 总结
    注解的方式只是减少了程序员编码的工作,其中的原理和之前介绍的用配置XML方式实现IOC注入的原理是一模一样的

AOP

传统Spring AOP

动态代理代理类实现

  1. 建立如下的目录结构(aspectj在后面会介绍,这里暂时不用)
    Spring IOC与AOP的简单实现
  2. 相应类的代码
    AccountDao与AccountImpl两个类和之前IOC里面的一模一样,直接拷贝即可。
public class proxyDemo implements InvocationHandler {

    private Object object;

    public proxyDemo(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().equals("select"))
        beforeAdvice();
        Object proxyObject = method.invoke(object,args);
        return proxyObject;
    }

    public void beforeAdvice(){
        System.out.println("前置建议:日志记录。。。");
    }
}
public class demo {

//    @Autowired
//    private AccountDAO accountDAO;


    @Test
    public void test(){

        AccountDAO accountDao = new AccountDAOImpl();

        Class class1 = accountDao.getClass();

        accountDao = (AccountDAO)Proxy.newProxyInstance(class1.getClassLoader(),class1.getInterfaces(),new proxyDemo(accountDao));

        accountDao.insert();

        accountDao.select();

    }
}
  1. 运行demo单元测试,结果如下
    Spring IOC与AOP的简单实现
  2. 总结
    传统AOP就是基于InvocationHandler这个接口实现的,其思想和JVM类似。实现AOP的原理如下:通过一个类实现InvocationHandler接口来编写自己的代理类,代理类通过invoke方法实现,对所有被代理类的方法的切入。这个方法有许多不足:所有被代理的类必须是实现接口的类,而且代理类只能实现对于被代理类的方法的切入。

XML配置实现

  1. 在Resources下面添加新的Spring config文件,并且进行相应的配置(因为需要用到aop的xml命名空间,这里需要在Spring官网里面进行相应的搜索)
    Spring IOC与AOP的简单实现
  2. 新增一个Proxy类,为了和之前的代理类区分
public class SpringAopProxyDemo implements AfterReturningAdvice {


    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("后置意见。。。");
    }
}
  1. 配置Spring config文件
<?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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="accountDAO" class="org.csu.spring.demo.aop.persistence.AccountDAOImpl"/>

    <bean id="demoProxy" class="org.csu.spring.demo.aop.proxy.SpringAopProxyDemo"/>

    <bean id="demoAfterProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="accountDAO"/>
        <property name="interceptorNames" value="demoProxy"/>
        <property name="proxyInterfaces" value="org.csu.spring.demo.aop.persistence.AccountDAO"/>
    </bean>
</beans>
  1. 修改demo测试单元的内容,并且运行得出结果
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-aop.xml")
public class demo {

    @Resource(name="demoAfterProxy")
    private AccountDAO accountDAO;


    @Test
    public void test(){
        accountDAO.insert();

    }
}

Spring IOC与AOP的简单实现

  1. 结论
    这个方式和之前的基于动态代理的实现原理一模一样,只不过把硬编码的方式编程类配置文件的方式。

基于AspectJ的Spring AOP

  1. 修改Spring config的内容
<?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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <aop:aspectj-autoproxy/>
    <context:component-scan base-package="org.csu.spring.demo.aop"/>
    <bean id="demoAspect" class="org.csu.spring.demo.aop.persistence.demoAspect"/>
</beans>
  1. 在aspectj文件夹下面创建一个切面类
@Component
@Aspect
public class demoAspect {

    //execution(访问修饰符,返回类型,类,方法,参数)
    @Before(value = "execution(* org.csu.spring.demo.aop.persistence.*.*(..))")
    public void before(){
        System.out.println("这是一个前置建议:安全验证。。。");
    }
}
  1. 修改demo测试单元代码,执行得出结果
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-aop.xml")
public class demo {

    @Autowired
    private AccountDAO accountDAO;


    @Test
    public void test(){

        accountDAO.select();

    }
}

Spring IOC与AOP的简单实现

  1. 结论
    aspectj的思想和之前传统的思想不太一样。动态代理以一个代理类为核心,所以每一个不同的被代理类都需要对应一个代理类,导致过程很繁琐;但是aspectj的核心为切面,每一个切面可以有对应多个被代理类的多个方法,这样的思想可以简化很多代码量!
相关标签: Spring