AOP
面向切面编程,学习之前先要了解一下代理模式:
代理模式(静态代理)
代理模式是AOP的底层实现,一般代理模式就是让一个真是对象想完成的核心功能由代理对象去进行修饰,这样的话就可以实现主要业务和次要业务的分离,而二者之间通过抽象对象来统一实现的规范,通过房东租房来详解一下代理模式:
抽象对象:租房(要租的房)
代理对象:中介
真实对象:房东
访问对象:客户(用来访问对象的人)
代码实现:
租房抽象接口:
package com.mao.proxy;
public interface Rent {
void rent();
}
房东实现类:
package com.mao.proxy;
//房东
public class Host implements Rent {
//房东租房,核心功能
public void rent() {
System.out.println("房东把房租出去了");
}
}
中介实现类:
package com.mao.proxy;
public class Proxy implements Rent{
private Host host;
public Proxy(Host host) {
this.host = host;
}
public void rent() {
seeHouse();
host.rent();
deal();
fare();
}
private void seeHouse(){
System.out.println("先带顾客看房");
}
private void deal(){
System.out.println("成交了");
}
private void fare(){
System.out.println("付钱交钥匙");
}
}
顾客实现类:
package com.mao.proxy;
public class Customer {
public static void main(String[] args) {
rent(new Proxy(new Host()));
}
public static void rent(Proxy proxy){
proxy.rent();
}
}
代理模式的好处:
- 可以使真实角色更纯粹,不用去关心公共业务。
- 公共业务交给了代理去实现,完成了业务的分工
- 公共业务发生扩展的时候,方便管理
缺点:
一个真实角色就要产生一个代理角色,代码量翻倍工作效率会变低。
这时候就需要使用动态代理这种模式了。
动态代理
- 动态代理和静态代理角色一样
- 动态代理是动态生成的,不是我们直接写好的
- 动态代理分为两类:基于接口的动态代理和基于类的动态代理
- 基于接口–JDK动态代理【此处使用,要了解Proxy(代理)和InvocationHandler(调用处理程序)】
- 基于类–cglib
- java字节码
动态代理的本质就是利用反射动态生成代理类,调用处理程序。
AOP
首先要导入一个包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
方式一:使用Spring的接口
UserService接口:
package com.mao.service;
public interface UserService {
void add();
void delete();
void update();
void query();
}
UserServiceImpl实现类:
package com.mao.service;
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("新增一个用户");
}
@Override
public void delete() {
System.out.println("删除一个用户");
}
@Override
public void update() {
System.out.println("更新一个用户");
}
@Override
public void query() {
System.out.println("查询一个用户");
}
}
我们要做的就是在每个方法之前和之后都切入一段日志,所以写了两个日志类:
Log.java
package com.mao.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Log implements MethodBeforeAdvice {
@Override
//method:要执行的目标对象方法
//args:参数
//target:对象
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"执行了"+method.getName()+"方法");
}
}
上述代码实现了MethodBeforeAdvice,说明了是在指定切入点的方法执行之前执行该接口的before方法。
AfterLog.java:
package com.mao.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
//Returning表示有返回值,AfterAdvice就不需要返回值
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的返回值为"+returnValue);
}
}
after表示在切入口之后,returning表示是带返回值的。
application.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注册bean -->
<bean id="log" class="com.mao.log.Log"></bean>
<bean id="afterLog" class="com.mao.log.AfterLog"></bean>
<bean id="userService" class="com.mao.service.UserServiceImpl"></bean>
<!-- 配置aop:需要导入aop的约束 -->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.mao.service.UserService.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
</beans>
配置bean、切入点,以及把切面指定到切入点。
输出内容:
注意:动态代理代理的一定是接口而不是实现类,所以getBean中使用的一定是接口
方式二:使用自定义类来实现AOP
自己编写一个自定义类来指定before和after时候的两个方法:
package com.mao.zdy;
public class Zdy {
public void before(){
System.out.println("=======方法开始之前=========");
}
public void after(){
System.out.println("=======方法执行之后=========");
}
}
application.xml:
<aop:config>
<aop:aspect ref="zdy">
<aop:pointcut id="pointcut" expression="execution(* com.mao.service.UserService.*(..))"/>
<!-- 指定before时执行的方法 -->
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
<!-- 指定after时执行的方法 -->
<aop:after method="after" pointcut-ref="pointcut"></aop:after>
</aop:aspect>
</aop:config>
执行结果:
成功!
第三种:通过注解实现
@Aspectj定义切面 @Before @After
Spring整合Mybatis
官网链接:http://mybatis.org/spring/
mybatis-spring:版本要对应
要导入的依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<!-- springjdbc支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<!-- mybatis-spring整合包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.0</version>
</dependency>
首先就是配置spring-dao(这里我直接用了application.xml,因为只有一个),因为到时候SSM整合的时候统一把spring-mvc和spring-dao两个xml import到application.xml之中,这样可以让每一个文件只负责对应的功能。
application.xml:
<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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- DataSource:使用spring的数据源替换Mybatis的配置 c3p0 dbcp druid 我们这里使用spring的jdbc
org.springframework.jdbc.datasource-->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/study?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="myz7758258"/>
</bean>
<!-- sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定数据源 -->
<property name="dataSource" ref="datasource"/>
<!-- 绑定Mybatis配置文件 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="mappers/*.xml"/>
</bean>
<!-- sqlSessionTemplate 相当于sqlSession的末班-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 只能使用构造器注入,因为它没有set方法 -->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!-- 把Mapper注入到spring容器中 -->
<bean id="userMapper" class="com.mao.dao.impl.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
这里相当于把原本mybatis-config.xml中配置的数据源当成了一个bean,注入到了SqlSessionFactoryBean之后,然后其他所有Mybatis相关的配置其实都可以放入其中,但是一般别名和settings都还是放在原来的mybatis配置文件中,可以用configLocation引入mybatis-config配置,而mapperLocations则是指定mappers的路径。有了sqlSessionFactory就要去创建SqlSession,这里用的是模板SqlSessionTemplate,这里就是通过构造器把sqlSessionFactory注入,然后只要在要使用的Mapper实现类中通过spring注入sqlSession(需要自己写set方法)即可实现。
UserMapper:
package com.mao.dao;
import com.mao.pojo.User;
import java.util.List;
public interface UserMapper {
List<User> getAllUser();
}
UserMapperImpl.java:
package com.mao.dao.impl;
import com.mao.dao.UserMapper;
import com.mao.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
UserMapperImpl implements UserMapper {
private SqlSessionTemplate sqlSession;
//写这个set方法是为了spring注入
public void setSqlSession(SqlSessionTemplate sqlSession){
this.sqlSession=sqlSession;
}
public List<User> getAllUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.getAllUser();
}
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mao.dao.UserMapper">
<select id="getAllUser" resultType="user">
select * from user;
</select>
</mapper>
测试代码及结果:
@Test
public void getAllUser() {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
System.out.println(userMapper.getAllUser());
}