spring Aop切面编程
Aop中的概念
target:目标类,即需要被代理的类
join-point:连接点:指的是那些有可能被拦截到的方法
pointcut:切入点,指的是已经被增强的连接点
advice:通知,增强的代码
weaving:织入,把advice织入到target创建proxy的过程
aspect:切面,是切入点和通知的结合
两种方式实现Aop切面编程:
基于xml方式的切面编程(以jdk动态代理为例)
1.首先需要定义切面类:里面是通知,即增强代码
public class MyAspect1 {
public void classId(){
System.out.println("这是zzj190703班的大神们:");
}
public void totalCount(){
System.out.println("zzj190703班共23人!");
}
public void afterReturn(Object message){
System.out.println("更新数据!"+ message);
}
public Object around(ProceedingJoinPoint joinPoint) {
Object ret = null;
try {
ret = joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("[环绕通知]这真是充实的一天, 早睡早起,方能养生!");
return ret;
}
}
2.在定义一个接口和一个实现类
接口
public interface StudentDao {
List<Student> findAllStudent();
int updateStudent();
}
3.实现类(目标类,即需要被代理的类):
里面的每一个方法都是一个连接点,被拦截到的点织入增强代码的点称为切入点
/**
* 这是xml方式的AOP
*/
public class StudentDaoImpl2 implements StudentDao {
@Autowired
private QueryRunner queryRunner;
@Override
public List<Student> findAllStudent() {
String sql = "select * from student";
List<Student> studentList = null;
try {
studentList = queryRunner.query(sql,new BeanListHandler<Student>(Student.class));
return studentList;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
@Override
public int updateStudent() {
String sql = "update student set sage=48 where sage=?";
try {
int num = queryRunner.update(sql,120);
return num;
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
}
4.最后配置xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
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/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.lanou" />
<context:property-placeholder location="jdbc.properties" />
<!--切面类:里面是通知,即要增强的代码-->
<bean id="myAspect" class="com.lanou.aspect.MyAspect1" />
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" autowire="constructor" />
<bean id="studentDao" class="com.lanou.dao.daoImpl.StudentDaoImpl2" autowire="byType"/>
<!--切面编程的配置-->
<aop:config>
<!--切入点的定义:里面有切入点表达式,即要拦截的方法-->
<aop:pointcut id="classid" expression="execution(* com.lanou.dao.daoImpl.StudentDaoImpl2.findAllStudent(..))"/>
<!--切面的定义:由切入点和通知组成-->
<aop:aspect ref="myAspect">
<aop:before method="classId" pointcut-ref="classid" />
<aop:around method="around" pointcut-ref="classid" />
<aop:after-returning method="afterReturn" pointcut-ref="classid" returning="message" />
</aop:aspect>
</aop:config>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
<property name="driverClassName" value="${jdbc.driver}" />
<property name="filters" value="stat" />
<property name="maxActive" value="20" />
<property name="initialSize" value="1" />
<property name="maxWait" value="60000" />
<property name="minIdle" value="1" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="20" />
<property name="asyncInit" value="true" />
</bean>
<!--方法一:-->
<!-- <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" c:ds-ref="dataSource" /> -->
<!--方法er-->
</beans>
需要注意的是:切面类和切面是不一样,切面类:里面定义的是通知方法,切面:是拦截连接点中指定的连接点(所拦截的连接点此时称为切入点)和通知组成.
基于注解的AOP
1.需要的依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
2.定义切面类
/**
* 这是注解的方式AOP
* 这是切面类
*/
@Aspect
@Component
public class MyAspect {
@Before("com.lanou.pointCut.MyPointCut.findAllStudent()")
public void classId(){
System.out.println("这是zzj190703班的大神们:");
}
@AfterReturning(value = "com.lanou.pointCut.MyPointCut.returnning()",returning = "message")
public void afterReturn(Object message){
System.out.println("更新数据!"+ message);
}
@After("com.lanou.pointCut.MyPointCut.findAllStudent()")
public void totalCount(){
System.out.println("zzj190703班共23人!");
}
}
3.切入点
public class MyPointCut {
@Pointcut("execution(* findAllStudent(..))")
public void findAllStudent(){
}
@Pointcut("execution(public * com.lanou.dao.daoImpl.*.updateStudent(..))")
public void returnning(){
}
}
4.然后是配置类
@Configuration
@ComponentScan(basePackages = "com.lanou")
@EnableAspectJAutoProxy
@PropertySource("classpath:jdbc.properties")
public class MyConfiguration {
@Value("${jdbc.user}")
private String user;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.driver}")
private String driver;
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername(user);
dataSource.setUrl(url);
dataSource.setPassword(password);
dataSource.setDriverClassName(driver);
return dataSource;
}
@Bean
public QueryRunner queryRunner(){
return new QueryRunner(dataSource());
}
}
5.接口
public interface StudentDao {
List<Student> findAllStudent();
int updateStudent();
}
6.实现
/**
* 这是注解的方式AOP
*/
public class StudentDaoImpl1 implements StudentDao {
@Autowired
private QueryRunner queryRunner;
@Override
public List<Student> findAllStudent() {
String sql = "select * from student";
List<Student> studentList = null;
try {
studentList = queryRunner.query(sql,new BeanListHandler<Student>(Student.class));
return studentList;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
@Override
public int updateStudent() {
String sql = "update student set sage=120 where sage=?";
try {
int num = queryRunner.update(sql,48);
return num;
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
}
注意上面的切面通知类和切入点还有目标类之间的对应关系.
关于AOP切面编程的原理
jdk动态代理模式
必要的配置
@Configuration
@ComponentScan(basePackages = "com.lanou")
@EnableAspectJAutoProxy
@PropertySource("classpath:jdbc.properties")
public class MyConfiguration {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.user}")
private String user;
@Value("${jdbc.driver}")
private String driver;
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setDriverClassName(driver);
dataSource.setPassword(password);
dataSource.setUsername(user);
return dataSource;
}
@Bean
public QueryRunner queryRunner(){
return new QueryRunner(dataSource());
}
}
1.定义切面
@Aspect
@Component
public class MyAspect {
public void befor(){
System.out.println("我在前面!");
}
public void after(){
System.out.println("我在后面!");
}
}
2.目标类的定义
一个接口
public interface StudentDao {
List<Student> findAllStudent();
void updateStudent();
void deleteStudent()
}
一个实现类
@Repository
public class StudentDaoImpl implements StudentDao {
@Autowired
private QueryRunner queryRunner;
@Override
public List<Student> findAllStudent() {
String sql = "select * from student where sage=?;";
try {
List<Student> studentList = queryRunner.query(sql, new BeanListHandler<Student>(Student.class),25);
return studentList;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
@Override
public void updateStudent() {
System.out.println("更新学生的信息!");
}
@Override
public void deleteStudent() {
System.out.println("删除学生的信息!");
}
}
3.jdk动态代理
/**
* 原理:创建一个新的对象,实现目标表类的所有接口
*/
public class MyBeanFactoryJdk {
public static StudentDao createStudentDao(){
//1.需要一个目标类
StudentDao studentDao = new StudentDaoImpl();
//2.需要一个切面类
MyAspect aspect = new MyAspect();
//3.生成切面,即生成代理:
StudentDao proxyStudentDao = (StudentDao) Proxy.newProxyInstance(MyBeanFactoryJdk.class.getClassLoader(), studentDao.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
aspect.befor();
Object obj = method.invoke(studentDao, args);
aspect.after();
return obj;
}
});
return proxyStudentDao;
}
}
cglib代理方式
上面的都一样只有代理部分不一样
/**
* 原理:创建目标类的子类
*/
public class MyBeanFactoryCglib {
public static StudentDao createStudentDao(){
//目标类
StudentDao studentDao = new StudentDaoImpl();
//切面类
MyAspect aspect = new MyAspect();
//cglib里面的核心类Enhancer
//1:创建Enhancer的对象
Enhancer enhancer = new Enhancer();
//2.选择继承的父类
enhancer.setSuperclass(studentDao.getClass());
//3.回调函数
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
aspect.befor();
Object obj = method.invoke(studentDao,objects);
aspect.after();
return obj;
}
});
StudentDao proxyStudentDao = (StudentDao) enhancer.create();
return proxyStudentDao;
}
}