黑马-Spring(IOC&DI) AOP

IOC(控制翻转)

概念

       把对象的创建、初始化、销毁等工作交给spring容器来做

案例

环境

步骤

1、  写一个HelloWorld类

2、  写一个配置文件   把hello类放到spring容器中

<?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-2.5.xsd">
    <!-- 
        beans
            一个bean代表一个类
          所以beans就是很多个类
     -->
     <!-- 
         一个类
         id  标示符   
         class 类的全名
      -->
    <bean id="helloWorld" class="com.itheima09.spring.ioc.helloworld.HelloWorld">
    </bean>
</beans>
applicationContext.xml

3、  客户端

package com.itheima09.spring.ioc.helloworld.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.itheima09.spring.ioc.helloworld.HelloWorld;

public class HelloWorldTest {
    @Test
    public void testHello(){
        HelloWorld helloWorld = new HelloWorld();
        helloWorld.hello();
    }
    
    @Test
    public void testHello_Spring(){
        /**
         * 1、启动spring容器
         * 2、从spring容器中把对象提取出来
         * 3、对象调用方法
         */
        //启动了spring容器了
        ApplicationContext context = 
                new ClassPathXmlApplicationContext("applicationContext.xml");
        //从spring容器中把helloWorld对象提取出来了
        HelloWorld helloWorld = (HelloWorld)context.getBean("helloWorld");
        helloWorld.hello();
    }
}
View Code

4、  说明:

Spring容器的作用就是为HelloWorld这个类创建对象

Spring容器的结构

创建对象

构造函数(用的最多)

静态工厂

关于静态工厂方法(https://www.jianshu.com/p/ceb5ec8f1174)

实例工厂

 

说明:

    给工厂类创建了一个对象helloWorldFactory,再利用工厂对象调用工厂方法

别名

  

    

对象的创建时机

案例

 

执行步骤为1,3,2

helloword构造函数为什么调用3次

以上两种情况是默认值,当spring容器启动的时候创建对象

在bean有这样一个属性

意义

       如果把lazy-init设置为true,则当spring容器启动的时候,检测不到任何错误,这样会存在很大的安全性隐患,所以一般情况下应该设置lazy-init为default/false。但是如果一个bean中有一个属性,该属性含有大量的数据,这个时候不希望该bean过早的停留在内存中。

这个时候需要用到lazy-init为true。

对象的scope

默认情况(scope=singleton)

在默认情况下放入到spring中的bean是单例的

       将来service层和dao层所有的类将放入到spring容器中,所以默认情况下这两个层的类的实例都是单例的,所以不能把数据声明到属性中。如果声明在属性中,将会成为共享的。

Scope为prototype

  

创建时机和scope的结合

Scope为prototype,lazy-init为true

       在context.getBean时创建对象

Scopse为prototype,lazy-init为false

       在context.getBean时创建对象,lazy-init为false失效

当scpose为prototype时,始终在context.getBean时创建对象

Scopse为singleton

       是默认情况

Init和destroy

 

说明:

1、  init方法是由spring内部执行的

2、  只有当spring容器关闭以后才能执行destroy方法,spring容器一般情况下是不会关闭的。只有当web容器销毁掉的时候才可能关闭掉,所以只要一个对象在spring容器中,在spring容器关闭之前,会一直保留。

3、  如果一个bean的配置是scope为”prototype”,则spring容器不负责销毁。

Spring容器做的事情

总结

       创建对象

1、  对象的创建方式

2、  对象的创建时机

3、  对象的创建的模式

4、  Init和destroy

5、  创建时机和创建模式的结合

DI(依赖注入)

概念

       给属性赋值

   

   

       给pid和name赋值的过程就是di

    

Xml

Setter方法

  

    

说明:

1、  spring容器实例化person和student两个对象

2、  利用java的反射机制调用属性的setter方法赋值   property的 name属性由setter决定

3、  在客户端利用context.getBean方法把spring容器中的一个对象获取了。

 

说明:

1、  启动spring容器

2、  实例化person对象和student对象

3、  给person中的属性赋值

4、  调用person的init方法初始化

5、  客户端利用context.getBean获取对象

说明:

1、  启动spring容器

2、  实例化person对象

3、  因为person对象依赖于student对象,所以在实例化person对象的时候必须实例化student对象,所以这个时候,在student对象上的lazy-init为true将失效

说明:

1、  启动spring容器

2、  实例化student

3、  在客户端执行context.getBean方法获取person对象

4、  实例化person对象,调用person的构造函数

5、  调用person中的setStudent方法,给person中的student赋值

6、  执行person中的init方法

7、  Person对象调用方法

构造器

   

说明:

1、  constructor-arg代表指定的构造器函数的其中的一个参数

2、  可以利用index,ref,value,type来指定唯一的构造器

3、  如果一个bean的配置中没有constructor-arg属性,则必须利用默认的构造函数创建对象。

4、  所以在写一个javabean的时候,应该提供属性的setter方法,默认的构造器,带参数的构造器

IOC和DI的意义

案例1

需求

       编写一个文档管理系统,在该系统中有如下的结构:

1、  Document:interface

readDocument方法

writeDocument方法

2、  WordDocument 是Document的实现类

readDocument

writeDocument

3、  ExcelDocument

readDocument

writerDocument

4、  PDFDocument

readDocument

writeDocument

5、  DocumentManager

Document  document;

readDocument()

writeDocument()

做法1

 

说明:

上述的代码是不完全的面向接口编程

做法2

 

说明:

      在代码端没有出现具体的类,完全的面向接口编程。

  在spring容器的配置文件中决定了documentManager中的接口的实现类是什么。而这个过程和java代码端没有关系。

 

案例2

需求

       把action调用service,service调用dao用spring来完成

实现

意义

       实现了完全的面向接口编程,在代码端没有要关系一个接口的实现类是什么。

注解

概念

1、  用来解释说明

2、  注解必须作用在类的某一个部分

3、  注解的作用域范围(java,class,jvm)

4、  注解解析器

自定义的注解

注解的使用

 

注解解析器

     

package com.itheima09.annotation;

import java.lang.reflect.Method;

import org.junit.Test;

/**
 * 注解解析器
 * @author zd
 *
 */
public class AnnotationParse {
    public static void parse(){
        Class classt = Itheima09.class;
        //在该类上存在ClassInfo注解
        if(classt.isAnnotationPresent(ClassInfo.class)){
            //从类上得到类的注解
            ClassInfo classInfo = (ClassInfo)classt.getAnnotation(ClassInfo.class);
            //输出该注解的name属性
            System.out.println(classInfo.name());
        }
        //获取该类的所有的方法
        Method[] methods = classt.getMethods();
        for(Method method:methods){
            //如果该方法上存在MethodInfo注解
            if(method.isAnnotationPresent(MethodInfo.class)){
                //获取该方法上面的methodinfo注解
                MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);
                //输出注解中的value属性
                System.out.println(methodInfo.value());
            }
        }
    }
    
    @Test
    public void test(){
        AnnotationParse.parse();
    }
}
View Code

Spring中的注解

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">
      <!-- 
          把person和student放入到spring容器中
       -->
      <bean id="person" class="com.itheima09.spring.di.annotation.Person"></bean>
      <bean id="student" class="com.itheima09.spring.di.annotation.Student"></bean>
      <!-- 
          启动依赖注入的注解解析器
       -->
      <context:annotation-config></context:annotation-config>
</beans>
applicationContext.xml
package com.itheima09.spring.di.annotation;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class PersonTest {
    @Test
    public void testPerson(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person = (Person)context.getBean("person");
        person.say();
    }
}
PersonTest

在spring的配置文件中

 

说明:

1、  启动spring容器

2、  spring容器内部创建了两个对象person和student

3、  当spring容器解析到

 

启动依赖注入的注解解析器:

1、  spring容器在容器中查找所有的bean(prerson,student)

2、  看哪些bean的属性上面是否有Resource注解

3、  如果属性上面有该注解,再次检查是否有name属性

4、  如果没有name属性,则会把该注解标注的属性的名称获取到和spring容器中的id做匹配,如果匹配成功,则赋值,如果匹配不成功,则按照类型(注解的变量的类型 和 所有bean的class类型)进行匹配,如果匹配成功,则赋值(不推荐,类型匹配要求bean类型唯一出现,否则错误),如果匹配不成功,则报错。(赋值指的是person的private属性student的赋值,在Person类中对private属性student前面加上 Resource注解)

5、  如果有name属性,则把name属性的值解析出来和spring容器中的id做匹配,如果匹配成功,则赋值,如果匹配不成功,则报错。

6、  从上述的步骤可以看出注解的效率比较低,xml的效率比较高,注解书写比较简单,xml书写比较复杂。

Spring容器的关于di的注解(spring自己的注解)

按照类型匹配

按照ID匹配

 

注解只能应用与引用类型

类扫描的注解

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    <!-- 
        把一个类放入到spring容器中,该类就是一个component
          在base-package指定的包及子包中扫描所有的类
     -->
      <context:component-scan base-package="com.itheima09.spring.scan.annotation">
      </context:component-scan>
</beans>
xml
package com.itheima09.spring.scan.annotation;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;

@Component("person")
public class Person {
    @Resource(name="student")
    private Student student;
    public void say(){
        this.student.say();
    }
}    
Person

步骤

    

  

说明:在指定的包及子包中扫描

 

流程分析

1、  启动spring容器

2、  Spring容器解析类扫描的注解解析器,在base-package指定的包及子包中查找所有的类

3、  查看哪些类上面是否含有@Component注解

4、  如果该注解的value的属性的值为空,则把类名的第一个字母变成小写,作为id值,放入到spring容器中

5、  如果该注解的value的属性的值不为空,则用value的属性的值作为id值,放入到spring容器中

6、  再次查找在spring容器中的类的所有的属性,按照@Resource的规则给属性赋值

说明

       使用了类扫描机制的做法,配置文件中的配置很简单了,但是效率越来越低。

继承

Xml的继承

      

注解的继承

 

Aop

目的

      

   让PersonDao的数据库的操作和Transaction事务的操作分离。

代理模式

静态代理模式

package com.itheima09.dao.proxy;

public interface PersonDao {
    public void savePerson();
}
interface
package com.itheima09.dao.proxy;

public class PersonDaoImpl implements PersonDao{
    public void savePerson() {
        System.out.println("save person");
    }
}
impl
package com.itheima09.dao.proxy;

import com.itheima09.dao.Transaction;

public class PersonDaoProxy implements PersonDao{
    private PersonDao personDao;
    private Transaction transaction;
    public PersonDaoProxy(PersonDao personDao,Transaction transaction){
        this.personDao = personDao;
        this.transaction = transaction;
    }
    public void savePerson() {
        /**
         * 1、开启事务
         * 2、进行save操作
         * 3、事务提交
         */
        this.transaction.beginTransaction();
        this.personDao.savePerson();
        this.transaction.commit();
    }
}
proxy
package com.itheima09.dao.proxy;

import org.junit.Test;

import com.itheima09.dao.Transaction;

public class ProxyTest {
    @Test
    public void testProxy(){
        /**
         * 创建PersonDaoImpl对象
         * 创建事务对象
         * 创建PersonDaoProxy对象
         */
        PersonDao personDao = new PersonDaoImpl();
        Transaction transaction = new Transaction();
        PersonDaoProxy personDaoProxy = new PersonDaoProxy(personDao, transaction);
        personDaoProxy.savePerson();
    }
}
proxytest

动态代理模式

Jdk动态代理

接口

目标类

      

拦截器

   

   

客户端

    

  

问题

1、  代理对象有多少方法,方法的名称是什么?

因为代理对象和目标类一样,同样的实现了接口,所以接口中有多少方法,代理对象中就有多少个方法,名称和接口中的方法的名称一样。

2、  拦截器中的invoke方法在什么时候执行的?

当在客户端,代理对象调用方法的时候,进入到了invoke方法

3、  拦截器中的invoke方法中的method参数在什么时候传递的值?

当在客户端,代理对象调用方法的时候,进入到了invoke方法,这个时候,method参数就是代理对象调用的方法。

4、  代理对象的方法体的内容是什么?

代理对象的方法体的内容就是invoke方法体的内容

代理对象的方法体:

1、  开启事务

2、  目标方法

3、  事务的提交

4、  代理对象的方法体就把事务和目标方法结合在一起了,这样做的目的就是为了让目标类的目标方法和事务的方法松耦合。

流程图

案例

     

目标接口
public interface SalaryManager {
    public void showSalary();
}    
View Code
目标类

    

package com.heima.dao.jdkproxy.salary;

public class SalaryManagerImpl implements SalaryManager{

    @Override
    public void showSalary() {
        System.out.println("正在查看工资");
        
    }

}
View Code

  

日志

   

package com.heima.dao.jdkproxy.salary;

public class Logger {
    public void logging(){
        System.out.println("logging");
    }
}
View Code
安全性框架

    

package com.heima.dao.jdkproxy.salary;

public class Security {
    public void security(){
        System.out.println("security");
    }
}
View Code

  

权限类
package com.heima.dao.jdkproxy.salary;

public class Privilege {
    private String access;

    public void setAccess(String access) {
        this.access = access;
    }

    public String getAccess() {
        return access;
    }
}
View Code

     

拦截器
package com.heima.dao.jdkproxy.salary;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class SalaryManagerInterceptor implements InvocationHandler{
    private Object target;
    private Logger logger;
    private Security security;
    private Privilege privilege;
    
    public SalaryManagerInterceptor(Object target, Logger logger,
            Security security, Privilege privilege) {
        
        this.target = target;
        this.logger = logger;
        this.security = security;
        this.privilege = privilege;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        this.logger.logging();
        this.security.security();
        if(this.privilege.getAccess().equals("admin")){
            method.invoke(target, args);
        }else{
            System.out.println("权限不足");
        }
        return null;
    }

}
View Code

拦截器把这些内容全部结合在一起了。

改进

可以把日志、安全性框架等作为一个接口出现

日志:

 

在拦截器中:

 

Cglib代理

1、  产生的代理类是目标类的子类

2、  是用字节码增强技术产生的代理类

案例

 

 jdkProxy缺点,  通知和目标方法结合在一起的过程,放在拦截器中,需要程序员自己指定(耦合性高),所以引入aop,可以再xml中配置,提高松耦合

Aop的概念

切面

       事务、日志、安全性的框架,权限等就是切面

通知

       切面中的方法就是通知

切入点

只有符合(表达式)切入点的条件,才能让通知和目标方法结合在一起

连接点

客户端调用的方法

织入

形成代理对象方法体的过程

Aop的意义

 

说明:

1、  在开发的过程中,日志、权限、安全性的框架、目标方法完全是松耦合的

2、  在形成代理对象的方法的过程中就把这几个结合在一起了

切入点表达式

3.1 execution
       由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的部件,并且在Spring中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的。如下是execution表达式的语法:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
       这里问号表示当前项可以有也可以没有,其中各项的语义如下:

modifiers-pattern:方法的可见性,如public,protected;
ret-type-pattern:方法的返回值类型,如int,void等;
declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
name-pattern:方法名类型,如buisinessService();
param-pattern:方法的参数类型,如java.lang.String;
throws-pattern:方法抛出的异常类型,如java.lang.Exception;
        如下是一个使用execution表达式的例子:

execution(public * com.spring.service.BusinessObject.businessService(java.lang.String,..))
       上述切点表达式将会匹配使用public修饰,返回值为任意类型,并且是com.spring.BusinessObject类中名称为businessService的方法,方法可以有多个参数,但是第一个参数必须是java.lang.String类型的方法。上述示例中我们使用了..通配符,关于通配符的类型,主要有两种:

*通配符,该通配符主要用于匹配单个单词,或者是以某个词为前缀或后缀的单词。
       如下示例表示返回值为任意类型,在com.spring.service.BusinessObject类中,并且参数个数为零的方法:

execution(* com.spring.service.BusinessObject.*())
       下述示例表示返回值为任意类型,在com.spring.service包中,以Business为前缀的类,并且是类中参数个数为零方法:

execution(* com.spring.service.Business*.*())
..通配符,该通配符表示0个或多个项,主要用于declaring-type-pattern和param-pattern中,如果用于declaring-type-pattern中,则表示匹配当前包及其子包,如果用于param-pattern中,则表示匹配0个或多个参数。
       如下示例表示匹配返回值为任意类型,并且是com.spring.service包及其子包下的任意类的名称为businessService的方法,而且该方法不能有任何参数:

execution(* com.spring.service..*.businessService())
       这里需要说明的是,包路径service..*.businessService()中的..应该理解为延续前面的service路径,表示到service路径为止,或者继续延续service路径,从而包括其子包路径;后面的*.businessService(),这里的*表示匹配一个单词,因为是在方法名前,因而表示匹配任意的类。

       如下示例是使用..表示任意个数的参数的示例,需要注意,表示参数的时候可以在括号中事先指定某些类型的参数,而其余的参数则由..进行匹配:

execution(* com.spring.service.BusinessObject.businessService(java.lang.String,..))
execution

https://www.cnblogs.com/zhangxufeng/p/9160869.html

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

       这里问号表示当前项可以有也可以没有,其中各项的语义如下:

  • modifiers-pattern:方法的可见性,如public,protected;
  • ret-type-pattern:方法的返回值类型,如int,void等;
  • declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
  • name-pattern:方法名类型,如buisinessService();
  • param-pattern:方法的参数类型,如java.lang.String;
  • throws-pattern:方法抛出的异常类型,如java.lang.Exception;
  • ..通配符,该通配符表示0个或多个项,主要用于declaring-type-pattern和param-pattern中,如果用于declaring-type-pattern中,则表示匹配当前包及其子包,如果用于param-pattern中,则表示匹配0个或多个参数。
    • *通配符,该通配符主要用于匹配单个单词,或者是以某个词为前缀或后缀的单词。

 

?表示可选可不选  可以不填

必须写 有2个:   1  ret-type-pattern:方法的返回值类型,如int,void等;

  • name-pattern:方法名类型,如buisinessService();
  • param-pattern:方法的参数类型,如java.lang.String;

代表所有的公共方法

 

代表所有的以set开头的方法

 

代表com.xyz.service包下的AccoutService类的所有的方法

 

代表com.xyz.service包下的所有的类的所有的方法

 

代表com.xyz.service包及子包下的所有的类的所有的方法

 

代表com.itheima.spring.aop.xml包下的所有的类的有三个参数,第一个参数为Long,第二个参数为String,第三个参数为任意类型的所有的方法

 

(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知

(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用

(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around

(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式

(5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类

Spring的aop

步骤

目标接口

   

目标类

    

切面

 

Spring的配置文件

执行流程

1、  context.getBean时,如果该类没有生成代理对象,则返回对象本身

2、  如果产生了代理对象,则返回代理对象

如果目标类实现了接口,则采用jdkproxy生成代理对象,如果目标类没有实现接口,则采用cglibproxy生成代理对象,而生成代理对象是由spring容器内部完成的。

通知

前置通知

在目标方法执行之前执行。

后置通知

       在目标方法执行之后执行

       可以获取目标方法的返回值

       当目标方法遇到异常,不执行

最终通知

       无论目标方法是否遇到异常都会执行,相当于代码中的finnaly

异常通知

       获取目标方法抛出的异常

环绕通知

       能够控制目标方法的执行

package com.heima.spring.aop.xml.advice;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class Transaction {
    public void begeinTransaction(JoinPoint joinPoint){
        System.out.println(joinPoint.getTarget());
        System.out.println(joinPoint.getArgs());
        System.out.println(joinPoint.getSignature().getName());
        System.out.println("begin transaction");
    }
    public void commit(JoinPoint joinPoint,Object val){
        System.out.println(val);
        System.out.println("commit");
    }
    public void finalyMethod(JoinPoint joinPoint){
        
        System.out.println("finaly method");
    }
    public void throwingMethod(JoinPoint jp,Throwable ex){
        System.out.println(ex.getMessage());
        System.out.println("异常");
        
    }
    public void arroundMethod(ProceedingJoinPoint pjp)throws Throwable{
        System.out.println("环绕通知aaa");
        pjp.proceed();
        System.out.println("环绕通知bbbb");
    }
}
xml

案例:异常处理

技术图

 

实现

切面

目标类

配置

从配置中可以看出,把service层所有的类当成目标类,只要service层所有的类的所有的方法抛出异常,则exceptionAspect中的异常通知就会获取到目标方法抛出的异常,所以在这里异常通知就是用来处理异常的,而且只有一个方法。并且该切面和所有的其他类都是松耦合的。

案例:权限的处理

技术图

 

组成

1、  写dao层和service层的类和接口

2、  自定义的注解@PrivilegeInfo

3、  注解解析器:解析目标方法上面的注解的name属性的值

4、  写一个权限类Privilege(name)

5、  写一个关于权限的判断的切面,在切面中写一个环绕通知

实现

       参照:/day03-02-itheima09-springAOP-xml-ex-privilege

扩展作业

利用spring的aop的环绕通知,记录如下的信息:

 目标类     目标方法     执行时间   方法的起始时间  方法的结束时间 

把上述的几个内容保存在数据库中,用jfreechar或者用table显示出来

AOP注解(了解)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
       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-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
    <context:component-scan base-package="com.itheima09.springaop.annotation.transaction"></context:component-scan>    
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
xml
package com.itheima09.springaop.annotation.transaction;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * @Aspect
 * ==
 * <aop:config>
 *         <aop:pointcut 
            expression="execution(* com.itheima09.springaop.annotation.transaction.PersonDaoImpl.*(..))" 
            id="aa()"/>
 * </aop:config>
 * @author zd
 *
 */
@Component("transaction")
@Aspect//说明该注解标注的类是一个切面类
public class Transaction {
    
    @Pointcut("execution(* com.itheima09.springaop.annotation.transaction.PersonDaoImpl.*(..))")
    private void aa(){} //方法标签    修饰符最好是private 返回值必须是void
    
    @Before("aa()")
    public void beginTransaction(){
        System.out.println("begin transaction");
    }
    @AfterReturning(value="aa()",returning="ex")
    public void commit(JoinPoint joinPoint,Object ex){
        System.out.println("commit");
    }
}
transaction
原文地址:https://www.cnblogs.com/hellowq/p/9862319.html