AOP entry road SSM practice learning --spring the third day _xml

Principles and concepts of the spring AOP's not go into details, use the time for Baidu, refreshing, dynamic proxies are involved here relevant, the method of enhanced without changing the original method of
my new stuff principle is:
to know about this thing, such as what can simplify, what role -> will use this simple thing -> help deepen few uses in order to grasp the whole process -> there is spare capacity related to looking backwards principle

First, the new maven project

Here Insert Picture Description

Second, add a dependency to pom.xml file

Packing way jar
added depends relatively simple, is dependent on a spring frame, a dependency is aspectj

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itheima</groupId>
    <artifactId>spring_day03_AOP</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
    </dependencies>
</project>

Third, the preparation of case needs category

Is very familiar with these interfaces and classes ...
but this time more than a Logger class to simulate the print log
the contents of the class has been modified
Here Insert Picture Description
IAccountDao Interface:

public interface IAccountDao {
    void saveAccount();
    int updateAccount(Integer id);
    void deleteAccount();
}

AccountDaoImpl categories:
Use annotations to add objects to the spring container ioc

@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {
    public void saveAccount() {
        System.out.println("新建账户");
    }
    public int updateAccount(Integer id) {
        System.out.println("更新了id为:"+id+"的用户");
        return id;
    }
    public void deleteAccount() {
        System.out.println("删除了一个账户");
    }
}

IAccountService Interface:
just like in the interface and IAccountDao

public interface IAccountService {
    void saveAccount();
    int updateAccount(Integer id);
    void deleteAccount();
}

AccountServiceImpl categories:
the same is by annotating ioc container spring let's create objects for our
use @Autowired injection IAccountDao objects

@Service("accountService")
public class AccountServiceImpl implements IAccountService {
    @Autowired
    private IAccountDao accountDao;

    public void saveAccount() {
        accountDao.saveAccount();
    }

    public int updateAccount(Integer id) {
        accountDao.updateAccount(id);
        return id;
    }
    public void deleteAccount() {
        accountDao.deleteAccount();
    }
}

New members of the Logger class:

@Component("logger")
public class Logger {
    public void beginLog(){
        System.out.println("开始执行方法...");
    }
    public void endLog(){
        System.out.println("方法执行结束...");
    }
}

Client categories:

public class Client {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        as.saveAccount();
        as.updateAccount(1);
        as.deleteAccount();
    }
}

Fourth, write bean.xml profile

Key in Chinese code comments (comment removed for easy observation symbols)
Special attention point 4 and several common execution notification wording

<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">
    <context:component-scan base-package="com.itheima"></context:component-scan>

    spring中基于XML的AOP配置步骤
        1、把通知Bean也交给spring来管理
        2、使用aop:config标签表明开始aop配置
        3、使用aop:aspect标签配置切面
                id属性:是给切面提供一个唯一标识
                ref属性:是指定通知类bean的id
        4、在aop:aspect标签的内部使用对应标签来配置通知的类型
                我们现在示例是让beginLog在切入点方法之前执行,所以是前置通知
                         让endLog在切入点方法执行之后执行,所以是后置通知
                aop:before 前置通知,在执行业务代码前执行
                aop:afterRetuning 返回通知,在业务代码成功执行后执行
                aop:after  后置通知,无论代码成功执行成功与否,都会执行(类似异常处理的finally)
                sop:afterThrowing 执行业务代码时发生异常,需要做的操作(类似异常处理的catch)
                    method属性:用于指定Logger类中哪个方法是前置通知
                    pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中的哪些方法增强

           切入点表达式的写法:
                关键字:execution(表达式)
                表达式:
                    访问修饰符 返回值 包名.包名.包名.....包名。类名.方法名(参数列表)
                标准的表达式写法:
                    public void com.itheima.service.impl.AccountServiceImpl.saveAccount()
                访问修饰符可以省略:
                    void com.itheima.service.impl.AccountServiceImpl.saveAccount()
                返回值可以用通配符 * 表示任意返回值
                    * com.itheima.service.impl.AccountServiceImpl.saveAccount()
                包名可以使用通配符,表示任意包,但是有几级包就要写几个*.
                    包名可以使用..来表示当前包以及其子包
                        * *..AccountServiceImpl.saveAccount()
                类名和方法名都可以使用*来使用通配
                        * *..*.*()
                参数列表:
                    可以直接写基本数据类型
                    引用数据类型要使用全限定类名,如Integer为java.lang。Integer
                    任意数据类型(包括无参)
                        使用(..)
                全通配写法:
                    * *..*.*(..)
                实际开发中切入点表达式的通常写法:
                    * com.itheima.service.impl.*.*(..)
        
    <aop:config>
        <aop:aspect id="log" ref="logger">
            <aop:before method="beginLog" pointcut="execution(* *..AccountServiceImpl.saveAccount())"></aop:before>
            <aop:after method="endLog" pointcut="execution(* *..AccountServiceImpl.saveAccount())"></aop:after>
        </aop:aspect>
    </aop:config>
</beans>

在这里还有一个东西,我们可以通过引用来简化我们的pointcut后面这一堆很长的重复很多的东西,来变成一个很短可读性也比较好的东西
使用aop:pointcut
id用来指定唯一标识,expression填写的是具体的表达式 也就是原来的pointcut里的内容
Here Insert Picture Description

五、运行Client.main()

可以看到在新建账户的开始和结束分别执行了beginLog和endLog方法
Here Insert Picture Description

环绕通知:

将这块环绕通知和上面四个通知分开,是因为这不是一个单独的通知,而是将上面四个通知的综合

Spring框架为我们提供了一个接口,proceedingJoinPoint.该接口有一个方法proceed()此方法就相当于明确调用切入点方法.
该接口可以作为环绕通知的方法参数,在程序执行时,Spring框架会为我们提供该接口的实现类供我们使用

Spring中的环绕通知:
它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式

可能看上面云里雾里的,看代码就很容易清楚是怎么一回事啦!
1、在Logger类中添加方法arroundLogger
注意这里一定要使用返回值,具体下面会说

public Object arroundLogger(ProceedingJoinPoint pdj){
        Object rtValue = null;
        try {
            Object args[] = pdj.getArgs();
            System.out.println("前置方法执行");
            rtValue=pdj.proceed(args);
            System.out.println("后置方法执行");
        } catch (Throwable throwable) {
            System.out.println("异常方法执行");
            throwable.printStackTrace();
        }finally {
            System.out.println("最终方法执行");
        }
        return rtValue;
    }

2、bean.xml

<aop:config>
        <aop:aspect id="log" ref="logger">
            <aop:around method="arroundLogger" pointcut="execution(* *..AccountServiceImpl.*(..))"></aop:around>
        </aop:aspect>
    </aop:config>

3、Client.main

public class Client {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        as.saveAccount();
        as.updateAccount(1);
        as.deleteAccount();
    }
}

4、执行结果
Here Insert Picture Description
可以看到对每个方法都执行了增强,所以这里可以看到,我们可以直接在一个方法中对一个方法直接编写各种通知,而不用通过编写4个方法以及4个xml来实现4个通知方式

上面提到,如果arroundLogger方法无返回值会怎么样呢?
返回值类型为void 方法内无return
Here Insert Picture Description
运行报错:

前置方法执行
新建账户
后置方法执行
最终方法执行
前置方法执行
更新了id为:1的用户
后置方法执行
最终方法执行
Exception in thread "main" org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for: public abstract int com.itheima.service.IAccountService.updateAccount(java.lang.Integer)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:226)
	at com.sun.proxy.$Proxy12.updateAccount(Unknown Source)
	at com.itheima.ui.Client.main(Client.java:12)

It can be seen after performing the update methods, delete methods do not perform, and produce an abnormal
look at the error message:Null return value from advice does not match primitive return type for: public abstract int com.itheima.service.IAccountService.updateAccount(java.lang.Integer)

I did not receive notice of the return type: int type position updateAccount of no return

Here we can see that, because of our updateAccount method has a return value of type int, but after the completion of the implementation of the method, we did not return to the return of the int, so we should return an object type for any possible return type parameter (here int, but there may be other methods or return String class object), the use of object receives and returns.

According to this example, and then look back spring surrounding the notification: It is a framework for us spring may be enhanced when the manual control method performed in the code, to deepen the understanding of this sentence.

Record stepped pit

Here Insert Picture Description
At first my pointcut is so written, meaning saveAccountImpl service call to the business layer of the cut-in, and found the results is this:
Here Insert Picture Description
After thinking, found that due to pointcut wrong, but also lead to the implementation of the AccountDaoImpl under the dao package inside saveAccount implementation class method, called twice before executing the method Logger
so when we write pointcut, because the method implementation and implementation classes of service interfaces of our dao interface is exactly the same, so we must indicate clear package name, and the name of the same class in a different package, at this time may be a wildcard *
to be noted that the recommended pointcut Chinese comments written in the above-mentioned code, using
* 包...包.包.*.方法名(..)

Published 23 original articles · won praise 0 · Views 594

Guess you like

Origin blog.csdn.net/SixthMagnitude/article/details/104148646