11、AOP
11.1、什么是AOP
AOP:(Aspect Oriented Programming)意为:面向切面编程。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生型,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率。
11.2、Aop在Spring中的作用
提供声明式事务;允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点,如日志,安全,缓存,事务等等…【假如这里要添加日志功能】
- 切面(ASPECT):横切关注点被模块化的特殊对象。即,它是一个类。【这个就是一个日志类】
- 通知(Advice):切面必须要完成的工作。即,它是类中 的一个方法。【日志类中的方法】
- 目标(Target):被通知对象。【一个要被代理的接口】
- 代理(Proxy):向目标对象应用通知之后创建的对象。【生成的代理类】
- 切入点(PointCut):切面通知执行的“地点”的定义。【指定方法在哪个地方执行】
- 连接点(JointPoint):与切入点匹配的执行点。
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5中类型的Advice:
11.3、使用Spring实现Aop
【重点】 使用AOP织入,需要导入一个依赖包!
<!--AOP -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.8.M1</version>
</dependency>
方式一:使用Spring的API接口【主要是SpringAPI接口实现】
-
要被代理的接口
package com.feng.service; public interface UserService { void add(); void delete(); void update(); void query(); }
-
被代理接口的实现类
package com.feng.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("查询了一条数据!"); } }
-
要在前面插入进去的类
package com.feng.log; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class Log implements MethodBeforeAdvice { //前置通知,即,将这个方法插入要执行的方法之前 //method:要执行的目标对象的方法 //args:参数 //target:目标对象 @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName()+"类的"+method.getName()+"方法,被执行了!"); } }
-
要在后面插入进去的类
package com.feng.log; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class AfterLog implements AfterReturningAdvice { //returnValue:返回值 @Override public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("执行了"+method.getName()+"返回的结果为"+returnValue); } }
-
在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="userservice" class="com.feng.service.UserServiceImpl"/> <bean id="log" class="com.feng.log.Log"/> <bean id="afterlog" class="com.feng.log.AfterLog"/> <!--方式一:使用原生Spring API接口--> <!--配置aop:需要导入aop的约束--> <aop:config> <!--切入点:expression:表达式,execution(锁定要执行切入方法的位置)--> <aop:pointcut id="pointcut" expression="execution(* com.feng.service.UserServiceImpl.*(..))"/> <!--执行环绕增加!--> <aop:advisor advice-ref="log" pointcut-ref="pointcut"/> <aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/> </aop:config> </beans>
-
测试
package com.feng; import com.feng.service.UserService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //动态代理代理的是接口:注意点 UserService userservice = (UserService) context.getBean("userservice"); userservice.add(); } }
方式二:自定义来实现AOP【主要是切面定义】
-
自定义的要切入程序中的类
package com.feng.diy; //自定义要切入原有程序中的类,即想要在原有程序上增加的功能 public class DiyPointCut { public void before(){ System.out.println("========方法执行前======="); } public void after(){ System.out.println("========方法执行后========"); } }
-
在XML中进行配置
<!--要被实行切入的接口的实现类--> <bean id="userservice" class="com.feng.service.UserServiceImpl"/> <!--方式二:自定义类切入程序中--> <bean id="diy" class="com.feng.diy.DiyPointCut"/> <aop:config> <!--自定义切面,ref 要引用的类 --> <aop:aspect ref="diy"> <!--切入点--> <aop:pointcut id="point" expression="execution(* com.feng.service.UserServiceImpl.*(..))"/> <!--通知--> <aop:before method="before" pointcut-ref="point"/> <aop:after method="after" pointcut-ref="point"/> </aop:aspect> </aop:config>
方式三:使用注解实现!
-
定义注解切面类
package com.feng.diy; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect //标记这个类是一个要切入程序中的一个切面 public class AnnotationPointCut { //定义要切入程序中的方法 @Before("execution(* com.feng.service.UserServiceImpl.*(..))") public void before(){ System.out.println("========方法执行前======="); } @After("execution(* com.feng.service.UserServiceImpl.*(..))") public void after(){ System.out.println("========方法执行后======="); } //环绕执行 @Around("execution(* com.feng.service.UserServiceImpl.*(..))") public void around(ProceedingJoinPoint pj) throws Throwable { System.out.println("环绕前"); //这里就是执行原程序中的方法,类似与过滤器中的,主要是让程序走下去 //得到一个签名 Signature signature = pj.getSignature(); System.out.println("这个签名为:"+signature); Object proceed = pj.proceed(); System.out.println("环绕后"); } }
-
在XML中打开注解实现
<!--方式三:--> <bean id="annotationPointCut" class="com.feng.diy.AnnotationPointCut"></bean> <!--打开注解支持 在Spring中:用jdk实现aop是默认的:proxy-target-class="false" 它默认就是false , 如果要用cglib:将proxy-target-class="true"设为true (但一般我们用默认的第一种实现就行,了解这里有这个关键字就行)--> <aop:aspectj-autoproxy proxy-target-class="false"/>
AOP:在不影响原来业务类的情况下,实现动态的增强!!
12、整合Mybatis
整合步骤:
-
导入相关jar包
- junit
- mybatis
- mysql
- spring
- aop织入
- mybatis-spring【new】
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.9</version> </dependency> <!--Spring操作数据库的话,还需要一个spring-jdbc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.8.M1</version> </dependency> <!--Spring和Mybatis整合的包 --> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency> </dependencies>
-
编写配置文件
-
测试
12.1、回忆mybatis
-
编写实体类
package com.feng.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String name; private String pwd; }
-
编写核心配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <properties resource="db.properties"> </properties> <typeAliases> <package name="com.feng.pojo"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/feng/mapper/UserMapper.xml"/> </mappers> </configuration>
-
编写接口
package com.feng.mapper; import com.feng.pojo.User; import java.util.List; public interface UserMapper { List<User> query(); }
-
编写Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.feng.mapper.UserMapper"> <select id="query" resultType="user"> select * from user </select> </mapper>
-
测试
package com.feng.mappertest; import com.feng.mapper.UserMapper; import com.feng.pojo.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; public class UserTest { @Test public void test01() throws IOException { String resources = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resources); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = build.openSession(true); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> query = mapper.query(); for (User user : query) { System.out.println(user); } } }
12.2、Mybatis-spring
- Mybatis-Spring官方文档:mybatis-spring –
- 将上面的Mybatis的配置文件在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"
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的配置
我们这里使用Spring提供的JDBC
将Mybatis中的数据源剥离出来,让Spring来管理
-->
<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/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--通过上面的数据源将 sqlSessionFactory 整出来-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--这里还可以绑定Mybatis的配置文件,尽管现在Mybatis中剩余的配置不多了
这样就使得它们两个联系起来了,其实完全可以不需要Mybatis了,在这里面就可以做Mybatis中可以做的任何配置了。
比如:添加Mapper映射,给实体类起别名等
-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/feng/mapper/UserMapper.xml"/>
</bean>
<!--这里的SqlSessionTemplate,就是我们在Mybatis中使用的sqlSession,
为了我们学习Mybatis的习惯,我们将他的id设为sqlSession
这里需要给SqlSessionTemplate这个类注入参数,一般在Spring中我们用属性注入就是set方法,
但是,这个类没有set方法,所以我们用构造注入。-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--这里通过sqlSessionFactory得到sqlSession-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!--将我们实现接口的实现类注入到spring中,在同时将我们上面得到的 SqlSessionTemplate 通过属性注入到这个类中
这样就是通过Spring将mybatis中的sqlSession对象注入到实现接口的类中,去调用接口中之前的注册到mapper.xml中的sql语句的方法
-->
<bean id="usermapperimpl" class="com.feng.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
这个配置最终是要得到一个SqlSessionTemplate对象,它与mybatis中的sqlSession对象是一样的。
-
写一个继承接口的实现类
package com.feng.mapper; import com.feng.pojo.User; import org.mybatis.spring.SqlSessionTemplate; import java.util.List; public class UserMapperImpl implements UserMapper{ //在原来的mybatis中我们的所有操作,都是通过sqlSession.getMapper来执行的 //现在Mybatis被Spring接管后,都用SqlSessionTemplate这个对象,但他跟sqlSession这个对象是一样的 //不过我们也可以在这里将它的变量名设为 sqlSession private SqlSessionTemplate sqlSession; public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } @Override public List<User> query() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> query = mapper.query(); return query; } }
在这个类中我们用 SqlSessionTemplate 去 getMappe ,并执行方法,而 SqlSessionTemplate这个对象,我们是通过Spring得到的,通过属性,将SqlSessionTemplate 注入到实体类中。
<!--将我们实现接口的实现类注入到spring中,在同时将我们上面得到的 SqlSessionTemplate 通过属性注入到这个类中 这样就是通过Spring将mybatis中的sqlSession对象注入到实现接口的类中,去调用接口中之前的注册到mapper.xml中的sql语句的方法 --> <bean id="usermapperimpl" class="com.feng.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean>
-
测试类:
@Test public void test02(){ //整合后的测试 ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml"); UserMapperImpl usermapperimpl = context.getBean("usermapperimpl", UserMapperImpl.class); List<User> query = usermapperimpl.query(); for (User user : query) { System.out.println(user); } } }
在这个测试类中,已经看不到了mybatis的有关信息,mybatis的作用已经在接口的实现类中通过spring注入的SqlSessionTemplate对象做完了。
总结:
- 编写数据源配置
- sqlSessionFactory
- sqlSessionTemplate
- 需要给接口加实现类
- 将自己写的实现类,注入到Spring中
- 测试使用即可!
一句话:通过spring管理Mybatis得到 SqlSessionTemplate对象 并注入到给接口加的实现类中,去执行接口中的方法。
第二种在实现类中的方式:
package com.feng.mapper;
import com.feng.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
@Override
public List<User> query() {
// SqlSession sqlSession = getSqlSession();
// UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// List<User> query = mapper.query();
// return query;
return getSqlSession().getMapper(UserMapper.class).query();
}
}
这种方式是继承: SqlSessionDaoSupport 类 用getSqlSession()得到 sqlSession,在在spring中注册该实体类,并注入 sqlSessionFactory 属性
<bean id="usermapperimpl2" class="com.feng.mapper.UserMapperImpl2">
<!-- 这种方法就不是注入sqlSession了,而是sqlSessionFactory-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>