Spring 框架的 AOP
Spring 框架的一个关键组件是面向方面的编程(AOP)框架。面向方面的编程需要把程序逻辑分解成不同的部分称为所谓的关注点。跨一个应用程序的多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。有各种各样的常见的很好的方面的例子,如日志记录、审计、声明式事务、安全性和缓存等。
说易懂些,在不影响原来业务功能的基础上,增加新的功能
AOP 术语
提供声明式事务;允许用户自定义切面
-
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
-
切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
-
通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
-
目标(Target):被通知对象。
-
代理(Proxy):向目标对象应用通知之后创建的对象。
-
切入点(PointCut):切面通知 执行的 “地点”的定义。
-
连接点(JointPoint):与切入点匹配的执行点。
通知的类型
Spring 方面可以使用下面提到的五种通知工作:
通知 | 描述 |
---|---|
前置通知 | 在一个方法执行之前,执行通知。 |
后置通知 | 在一个方法执行之后,不考虑其结果,执行通知。 |
返回后通知 | 在一个方法执行之后,只有在方法成功完成时,才能执行通知。 |
抛出异常后通知 | 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。 |
环绕通知 | 在建议方法调用之前和之后,执行通知。 |
实现自定义方面
方法 | 描述 |
---|---|
XML Schema based | 方面是使用常规类以及基于配置的 XML 来实现的。 |
@AspectJ based | @AspectJ 引用一种声明方面的风格作为带有 Java 5 注释的常规 Java 类注释。 |
下面使用Spring实现AOP:
第一种:使用Spring的API 接口 【主要SpringAPI接口实现】
建立UserService接口及其接口实现类
public interface UserService {
public void add();
public void submit();
}
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加方法");
}
public void submit() {
System.out.println("减少方法");
}
}
建立配置类
public class config implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"方法");
}
}
public class configAfter implements AfterReturningAdvice {
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println(o1.getClass().getName()+"的"+method.getName()+"方法");
}
}
建立配置文件application Context.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:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启属性注解-->
<!--<context:annotation-config/>-->
<!--扫描包:自动注入-->
<!--<context:component-scan base-package="com.feng"/>-->
<bean id="userService" class="com.feng.service.Impl.UserServiceImpl"/>
<bean id="before" class="com.feng.service.config.config"/>
<bean id="after" class="com.feng.service.config.configAfter"/>
<!--aop的配置-->
<aop:config>
<!--切入点,那些方法要被增强
expression:表达式就是类名的全称 * com.kuang.service.serviceImpl.add() -->
<aop:pointcut id="pc-userservice" expression="execution(* com.feng.service.Impl.UserServiceImpl.*(..))"/>
<!--环绕-->
<aop:advisor advice-ref="before" pointcut-ref="pc-userservice"></aop:advisor>
<aop:advisor advice-ref="after" pointcut-ref="pc-userservice"></aop:advisor>
</aop:config>
</beans>
对其进行测试:
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
输出效果如:
com.feng.service.Impl.UserServiceImpl的add方法
增加方法
com.feng.service.Impl.UserServiceImpl的add方法
第二种: 自定义来实现AOP 【主要是切面定义】
首先保持上面的UserService与其实现类不变
在建立一新的自定义的配置类
public class DiyPointCut {
public void before(){
System.out.println("=======方法执行前=========");
}
public void after(){
System.out.println("=======方法执行后==========");
}
}
在applicationContext.xml中编写
<bean id="diy" class="com.feng.service.config.DiyPointCut"/>
<aop:config>
<aop:aspect ref="diy">
<aop:pointcut id="pc-userservice" expression="execution(* com.feng.service.Impl.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="pc-userservice"/>
<aop:after method="after" pointcut-ref="pc-userservice"/>
</aop:aspect>
</aop:config>
将其测试:
输出结果如下:
=======方法执行前=========
增加方法
=======方法执行后==========
第三种:使用注解实现AOP思想!
首先保持上面的UserService与其实现类不变
在建立一新的自定义的配置类
//注解实现增强
@Aspect
public class AnnotationPointCut {
@Before("execution(* com.feng.service.Impl.UserServiceImpl.*(..))")
public void before(){
System.out.println("=======方法执行前=====");
}
@After("execution(* com.feng.service.Impl.UserServiceImpl.*(..))")
public void after(){
System.out.println("=======方法执行后=====");
}
//环绕增强
@Around("execution(* com.feng.service.Impl.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("=======环绕前=====");
System.out.println(joinPoint.getSignature());
Object proceed = joinPoint.proceed();
System.out.println("=======环绕后=====");
System.out.println(proceed);
}
}
在配置文件中写
<!-- 1.配置目标对象 -->
<bean id="userService" class="com.feng.service.Impl.UserServiceImpl"/>
<!-- 2.配置通知对象 -->
<bean id="annotationPointCut" class="com.feng.service.config.AnnotationPointCut"/>
<!-- 3.开启使用注解完成织入 -->
<aop:aspectj-autoproxy/>
将其测试:
输出结果如下:
=======环绕前=====
void com.feng.service.UserService.add() //为System.out.println(joinPoint.getSignature());的输出
=======方法执行前=====
增加方法
=======环绕后=====
null //System.out.println(joinPoint.getSignature());
=======方法执行后=====
整合Mybatis
参考学习网站:http://mybatis.org/spring/zh/getting-started.html
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和 SqlSession
并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的 DataAccessException
。最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。
MyBatis-Spring 需要以下版本:
MyBatis-Spring | MyBatis | Spring框架 | Spring Batch | Java |
---|---|---|---|---|
2.0 | 3.5+ | 5.0+ | 4.0+ | Java 8+ |
1.3 | 3.4+ | 3.2.2+ | 2.1+ | Java 6+ |
要使用 MyBatis-Spring 模块,只需要在类路径下包含 mybatis-spring-2.0.3.jar
文件和相关依赖即可。如果使用 Maven 作为构建工具,仅需要在 pom.xml 中加入以下代码即可:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
相关步骤:
- 导入相关jar包
- junit
- mybatis
- mysql数据库
- spring相关的
- aop织入
- mybatis-spring 【new】
- 编写配置文件
- 测试
具体内容:
创建一个maven项目,
- 依赖:
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</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.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<!--Spring jdbc(new)-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<!--aop aspectjweaver-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<!--mybatis-spring-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
</dependencies>
- 建立一个User实体类(对应在数据库中也有一张User表)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
- 建立UserMapper的接口以及实现类
public interface UserMapper {
//获取用户列表
public List<User> getUser();
}
public class UserMapperImpl implements UserMapper {
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession){
this.sqlSession=sqlSession;
}
public List<User> getUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.getUser();
}
}
- 编写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.feng.mapper.UserMapper">
<select id="getUser" resultType="User">
select * from user
</select>
</mapper>
- 配置applicationContext.xml文件
<!--集合MyBatis-->
<!--1 数据源整合-->
<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=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--2 注入sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliases" value="com.feng.pojo.User"/>
<property name="mapperLocations" value="classpath:com/feng/mapper/xml/*.xml"/>
</bean>
<!--3 创建SQL session-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
<!--4 将sqlSession放入Bean-->
<bean id="userMapperImpl" class="com.feng.mapper.impl.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
- 写测试类
@Test
public void test1() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapperImpl o = (UserMapperImpl) context.getBean("userMapperImpl");
List<User> user = o.getUser();
for (User user1 : user) {
System.out.println(user1);
}
}
- 打印数据库中User表中用户信息
User(id=1, name=小宋, pwd=123456)
User(id=2, name=小子, pwd=123111)
User(id=3, name=小红, pwd=111111)
User(id=4, name=小黑, pwd=010101)
一个使用 MyBatis-Spring 的其中一个主要原因是它允许 MyBatis 参与到 Spring 的事务管理中。而不是给 MyBatis 创建一个新的专用事务管理器,MyBatis-Spring 借助了 Spring 中的 DataSourceTransactionManager 来实现事务管理。
一旦配置好了 Spring 的事务管理器,你就可以在 Spring 中按你平时的方式来配置事务。并且支持 @Transactional 注解和 AOP 风格的配置。在事务处理期间,一个单独的 SqlSession
对象将会被创建和使用。当事务完成时,这个 session 会以合适的方式提交或回滚。
事务配置好了以后,MyBatis-Spring 将会透明地管理事务。这样在你的 DAO 类中就不需要额外的代码了。
事务ACID原则:
- 原子性
- 一致性
- 隔离性
- 多个业务可能操作同一个资源,防止数据损坏
- 持久性
- 事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化的写到存储器中!
上手测试:
首先在UserMapper接口中加入两个方法
public interface UserMapper {
public List<User> getUser();
public int addUser(User user);
public int deleteUser(int id);
}
在UserMapperImpl实现类中,测试:在获得用户信息功能中同时加入添加与删除用户,若不开启事务,是否会执行
public class UserMapperImpl implements UserMapper {
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession){
this.sqlSession=sqlSession;
}
public List<User> getUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//一组操作
User user = new User(5,"天天","111");
mapper.addUser(user);
mapper.deleteUser(4);
return mapper.getUser();
}
public int addUser(User user) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.addUser(user);
}
public int deleteUser(int id) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.deleteUser(id);
}
}
在UserMapper.xml配置文件中写sql
<?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.feng.mapper.UserMapper">
<select id="getUser" resultType="User">
select * from user
</select>
<insert id="addUser" parameterType="User">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>
<delete id="deleteUser" parameterType="int">
<!--将delete拼写错误进行测试-->
deletes from user where id=#{id}
</delete>
</mapper>
测试与上面一致
此时数据库中信息为
测试结果分析:
- 首先因为sql中delete的拼写错误,控制台报错!
- 但是因为没有开启事务,在报错的同时,数据库中的信息进行了修改!
- 不符合事务的原则,我们须要让一组操作保证同成功同失败!
- 在applicationContext.xml中进行事务配置
<!--整合事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource"/>
</bean>
<!--事务配置增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--AOP配置-->
<aop:config proxy-target-class="true">
<aop:pointcut id="txPointCut" expression="execution(* com.feng.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
5.因为还未改动sql语句,再次测试
虽然是报错,但是不会将数据库中的信息进行更改
- 将sql语句进行改正,再次测试,就会输出正确结果
User(id=1, name=小宋, pwd=123456)
User(id=2, name=小子, pwd=123111)
User(id=3, name=小红, pwd=111111)
User(id=5, name=天天, pwd=111)
SpringMVC入门
建立SpringMVC的第一个Hello程序!
- 建立一个空的Maven项目,并且添加web框架支持
- 导入依赖
<?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.feng</groupId>
<artifactId>SpringMVC01</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
</dependencies>
</project>
- 配置Tomcat服务器
- 建立Controller层及其下HelloController类
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(Model model){
System.out.println("aaa");
model.addAttribute("msg","Hello,SpringMVC");
return "hello";
}
}
- 在WEB-INF目录下建立一个存放自己页面的目录,如views,在其写hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello</title>
</head>
<body>
<h1>Hello</h1>
<h1>${msg}</h1>
</body>
</html>
- 在resources目录下建立spring-servlet.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:mvc="http://www.springframework.org/schema/mvc"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--固定配置-->
<!--扫描包,控制层,和前端交互的 controller-->
<context:component-scan base-package="com.feng.controller"/>
<!--注解驱动开启-->
<mvc:annotation-driven/>
<!--在web开发中,我们一般存在静态资源,js,css,img,,,-->
<mvc:default-servlet-handler/>
<!--重点:视图解析器 internalResourceViewResolver-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
- 在web.xml中进行配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--注册 DispatherServlet ,由Spring提供-->
<servlet>
<servlet-name>DispatherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--关联一个SpringMVC的配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
<!--服务器启动的时候就启动-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 运行tomcat
(1) 出现首页
(2) 在地址栏路径后输入hello
且在控制台会输出aaa。