Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 **IoC(Inverse Of Control: 反转控制)**和 **AOP(Aspect Oriented Programming:面向切面编程)**为内核,提供了展现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多 著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。
通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造 成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可 以更专注于上层的应用。
通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付。
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理, 提高开发效率和质量。
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可 做的事情。
Spring 可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz 等)的直接支持
Spring 对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的 使用难度大为降低。
就是通过工厂模式+单例 解耦
解耦:
/**
* 程序的耦合
* 耦合:
* 类之间的依赖
* 方法之间的依赖
* 解耦:
* 降低程序之间的依赖
* 实际开发:
* 编译期不依赖,运行时才依赖
* 解耦思路
* 避免使用new关键字,使用反射创建
* 通过读取配置文件获取要创建的对象全限定类名
*/
package com.hpy.factory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* @author hpy
* 创建Bean对象的工厂
* Bean:在计算机英语中有 可重用组件的定义。
* javaBean:用java语言编写的可重用组件
* javaBean >> 实体类 (远大于)
* 1.需要一个配置文件 配置Service和dao
* 配置文件内容:
* 1.全限定类名
* 2.唯一标志 = 全限定类名 (key:value)
* 选择 properties或xml
* 2.通过读取配置文件的中内容,反射创建Bean对象
*/
public class BeanFactory {
/**
定义一个properties对象
*/
private static Properties properties;
/**
* 定义 一个map 存放我们要创建的对象 称之为容器
*
*/
private static Map<String,Object> beans;
static {
try {
//实例化对象
properties = new Properties();
//获取properties流对象 用类加载器
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("factory.properties");
properties.load(in);
//实例化容器
beans = new HashMap<String, Object>();
//取出所有的keys
Enumeration keys = properties.keys();
//遍历枚举
while (keys.hasMoreElements()){
//取出每个key
String key = keys.nextElement().toString();
//根据key获取value
String property = properties.getProperty(key);
//反射创建对象
Object newInstance = Class.forName(property).newInstance();
//放入beanMap
beans.put(key,newInstance);
}
} catch (IOException e) {
throw new ExceptionInInitializerError("初始化失败");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 根据Bean名称获取对象
* @param beanName Bean名称
* @return 方法
*/
public static Object getBean(String beanName){
return beans.get(beanName);
}
}
<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.xsd">
<bean id="userService" class="com.hpy.service.Impl.UserServiceImpl"/>
<bean id="userDao" class="com.hpy.dao.impl.UserDaoImpl"/>
beans>
<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.xsd">
<bean id="userService" class="com.hpy.service.Impl.UserServiceImpl"/>
<bean id="InstanceFactory" class="com.hpy.factory.factory"/>
<bean id="userService1" factory-bean="InstanceFactory" factory-method="getUserService"/>
<bean id="userService2" class="com.hpy.factory.factory" factory-method="getUserServiceStatic"/>
<bean id="userService3" class="com.hpy.service.Impl.UserServiceImpl" init-method="init" destroy-method="destroy"/>
<bean id="userService4" class="com.hpy.service.Impl.UserServiceImpl" scope="prototype"
init-method="init" destroy-method="destroy"/>
beans>
<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.xsd">
<bean id="now" class="java.util.Date"> bean>
<bean id="userService" class="com.hpy.service.Impl.UserServiceImpl">
<constructor-arg name="name" value="测试"> constructor-arg>
<constructor-arg name="age" value="18"> constructor-arg>
<constructor-arg name="birthday" ref="now"> constructor-arg>
bean>
<bean id="userService1" class="com.hpy.service.Impl.UserServiceImpl2">
<property name="name" value="测试"> property>
<property name="age" value="13"> property>
<property name="birthday" ref="now"> property>
bean>
<bean id="userService2" class="com.hpy.service.Impl.UserServiceImp3">
<property name="myStrs">
<array>
<value>AAAvalue>
<value>AABvalue>
<value>AACvalue>
array>
property>
<property name="myList">
<list>
<value>AAAvalue>
<value>AABvalue>
<value>AACvalue>
list>
property>
<property name="mySet">
<set>
<value>AAAvalue>
<value>AABvalue>
<value>AACvalue>
set>
property>
<property name="myMap">
<map>
<entry key="ccc" value="23"> entry>
map>
property>
<property name="myprops">
<map>
<entry key="ccc" value="23"> entry>
map>
property>
bean>
beans>
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
private QueryRunner runner;
public void setRunner(QueryRunner runner) {
this.runner = runner;
}
<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.xsd">
<bean id="userService" class="com.hpy.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"> property>
bean>
<bean id="userDao" class="com.hpy.dao.impl.UserDaoImpl">
<property name="runner" ref="runner"> property>
bean>
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<constructor-arg name="ds" ref="jdbc">constructor-arg>
bean>
<bean id="jdbc" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"> property>
<property name="jdbcUrl" value="jdbc:mysql:///mybaitis"> property>
<property name="user" value="root"> property>
<property name="password" value="1234"> property>
bean>
beans>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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">
<context:component-scan base-package="com.hpy"> context:component-scan>
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<constructor-arg name="ds" ref="jdbc">constructor-arg>
bean>
<bean id="jdbc" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"> property>
<property name="jdbcUrl" value="jdbc:mysql:///mybaitis"> property>
<property name="user" value="root"> property>
<property name="password" value="1234"> property>
bean>
beans>
package com.hpy.service.Impl;
import com.hpy.dao.UserDao;
import com.hpy.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
/**
* 用户业务层实现类
* @author hpy
* 注解实现创建对象
*
* 用于创建对象的注解
* 和 相同
* 1. Component;
* 属性:
* value:指定bean的id 默认值是当前类名的首字母小写 当前类即 userService
* 2. Controller:一般用于表现层
* 3. Service:一般用于业务层
* 4. Repository:一般用于持久层
* 以上三个注解他们的作用和属性和Component一样,他们三个是Spring框架为我们提供的明确的三层使用注解
* 使我们的三层对象更加清晰
* 用于注入数据的注解
* 作用相当于 ;
* 5. Autowired
* 作用:自动按照类型注入,只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
* 如果没有任何bean的类型和要注入的类型匹配 则报错
* 如果有多个类型匹配时:先用变量名查找key 如果Key有一样的也会匹配注入
* 出现位置:可以是变量上可以是方法上
* 6. Qualifier
* 作用:在按照类中注入的基础上再按照名称注入,它给类成员注入时不能单独使用,但是给方法参数注入时可以使用
* 属性
* value:用于指定bean的id
* 给类成员变量使用时 要配合 @Autowired
* 7. Resource:@Resource(name = "userDao1")
* 作用直接按照id注入
* 属性:
* name:bean的id
* 以上三个都只能注入bean类型的数据,而基本类型和String无法实现
* 8. Value:
* 作用:用于基本类型和String类型的注入
* 属性:
* value:用于指定数据的值,可以使用spEL
* spEL:${表达式}
* 【在使用注解注入时,set方法不是必须的】
*
* 【集合等只能用xml实现】
* 用于改变作用范围的
*
* 9. Scope:
* 作用:用于指定bean的作用范围
* 属性:
* value: singelton prototype
* 生命周期相关[了解]
*
* 10. PreDestroy:用于指定销毁方法
* 11. PostConstruct:用于指定初始化方法
*/
@Service("userService")
//@Scope("prototype")
public class UserServiceImpl implements UserService {
/* @Autowired
@Qualifier("userDao1")*/
@Resource(name = "userDao1")
private UserDao userDao;
@PostConstruct
public void init(){
System.out.println("初始化方法");
}
@PreDestroy
public void destroy(){
System.out.println("销毁");
}
public void saveUser() {
userDao.saveUser();
}
}
package config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
/**
* 是一个配置类,他的作用和bean.xml是一样的
* 为了把剩下的两个标签解决
* spring中的新注解
*
* 1.Configuration
* 作用:指定当前类是一个配置类
* 当配置类作为AnnotationConfigApplicationContext()的参数时,可以不写,不想写的话可以在调用时添加类
*
* 2.ComponentScan:
* 作用:通过注解指定spring在容器中扫描的包
* 属性 ;
* value: 他和basePackages作用是一样的
* 3.Bean
* 作用:
* 把当前的方法的返回值放到spring的IOC容器的value中
* 属性:
* name:用于指定bean的id 默认值是 当前方法的名称
* 当我们使用注解配方法时,当方法有参数,会去容器查找有没有可用的Bean对象
* 4.Import
* 作用:用于导入其他的配置类
* 属性
* value: 放入其他配置文件的字节码 有import的类为父配置类 导入的都是子配置类
* 5.PropertySource
* 作用:
* 指定properties文件的位置
* value:
* 指定文件的名称和路径
* 关键字:classpath,表示类路径下
*/
@ComponentScan("com.hpy")
@Import(JDBCconfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
}
package config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
/**
* 和spring连接数据库相关的配置类
*/
public class JDBCconfig {
@Value("${driver}")
private String driver;
@Value("${jdbcUrl}")
private String url;
@Value("${userName}")
private String username;
@Value("${passWord}")
private String password;
/**
* 创建一个QueryRunner对象r
* @param dataSource 数据源
* @return QueryRunner
* 若有两个数据源 使用 @Qualifier("dataSource")
*/
@Bean(name = "runner")
@Scope("prototype")
public QueryRunner createQueryRunner(@Qualifier("dataSource") DataSource dataSource){
return new QueryRunner(dataSource);
}
/**
* 创建数据源对象
* @return 数据源
*
*/
@Bean(name = "dataSource")
public DataSource createDataSource(){
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
try {
comboPooledDataSource.setDriverClass(driver);
comboPooledDataSource.setJdbcUrl(url);
comboPooledDataSource.setUser(username);
comboPooledDataSource.setPassword(password);
return comboPooledDataSource;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
import config.SpringConfiguration;
import com.hpy.domain.User;
import com.hpy.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Date;
import java.util.List;
/**
* Spring整合的junit
* 1.通过注解替换junit的main
* RunWith
* 2.告知Spring运行器 Spring和ioc创建时xml还是注解 并说明位置
* ContextConfiguration:
* value: 指定xml的文件位置 加上classpath关键字 表示在类路径下
* classes 执行的注解配置文件字节码
* 当使用spring 5.x版本 要求junit的jar版本必须是4.12和以上
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class test {
@Autowired
private UserService userService;
@Test
public void testSel(){
List<User> all = userService.findAll();
for (User user : all) {
System.out.println(user);
}
}
@Test
public void testUpd(){
User user = new User();
user.setUsername("韩鹏宇upd");
user.setSex("男");
user.setBirthday(new Date());
user.setAddress("哈尔滨冠希");
user.setId(41);
userService.updateUser(user);
}
@Test
public void testIns(){
User user = new User();
user.setUsername("韩鹏宇ins");
user.setSex("男");
user.setBirthday(new Date());
user.setAddress("哈尔滨冠希");
userService.addUser(user);
}
@Test
public void testDel(){
userService.delUser(46);
}
}
package com.hpy.util;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* connection工具类 实现和线程绑定
*/
@Component("connectionUtil")
public class ConnectionUtil {
private ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
@Resource(name = "jdbc")
private DataSource dataSource;
/**
* 获取当前线程上的连接
* @return con
*/
public Connection getConnection(){
//1.从线程获取
Connection con = threadLocal.get();
if (con == null){
//从数据源获取连接 存入threadLocal中
try {
con = dataSource.getConnection();
threadLocal.set(con);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
return con;
}
/**
* 把连接和线程解绑
*/
public void removeConnection(){
threadLocal.remove();
}
}
package com.hpy.util;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* 事务管理相关的工具类
* 包含了开启 提交 回滚 释放连接 事务
*/
@Component("tranManager")
public class TranManager {
@Resource(name = "connectionUtil")
private ConnectionUtil connectionUtil;
/**
* 开始事务
*/
public void beginTran() {
Connection connection = connectionUtil.getConnection();
try {
connection.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 提交事务
*/
public void commit(){
Connection connection = connectionUtil.getConnection();
try {
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 回滚事务
*/
public void rollback(){
Connection connection = connectionUtil.getConnection();
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 释放资源
*/
public void release(){
Connection connection = connectionUtil.getConnection();
try {
connection.close();
connectionUtil.removeConnection();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 事务控制都该在业务层
* 为了防止转账的异常 使用 ThreadLocal绑定Connection
*
*/
@Service("userService")
public class UserServiceImpl implements UserService {
@Resource(name = "userDao")
private UserDao userDao;
@Resource(name = "tranManager")
private TranManager tranManager;
@Override
public void transfer(String sourceName, String targetName, Float money) {
try{
tranManager.beginTran();
//1.根据名称查询到转账人账户信息
Account sourceAccount = userDao.findAccountByName(sourceName);
//2.根据名称查到收账人信息
Account targetAccount = userDao.findAccountByName(targetName);
//3.转出减钱
sourceAccount.setMoney(sourceAccount.getMoney()-money);
//4.收到加钱
targetAccount.setMoney(targetAccount.getMoney()+money);
//5.更新转出账户
userDao.updateMoney(sourceAccount);
//若加上这句 则抛异常不继续执行下面的更新操作 导致数据库异常
//int i = 1/0;
//6.更新转入账户
userDao.updateMoney(targetAccount);
tranManager.commit();
}catch (Exception e){
System.out.println("回滚");
tranManager.rollback();
throw new RuntimeException(e);
}finally {
tranManager.release();
}
}
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Resource(name = "runner")
private QueryRunner runner;
@Resource(name = "connectionUtil")
private ConnectionUtil connectionUtil;
@Override
public Account findAccountByName(String name) {
try {
List<Account> list = runner.query(connectionUtil.getConnection(),"select * from account_spring where name = ?",new BeanListHandler<Account>(Account.class),name);
if (list == null || list.size() == 0){
return null;
}
if (list.size() > 1){
throw new RuntimeException("结果集合不唯一");
}
return list.get(0);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void updateMoney(Account account) {
try {
runner.update(connectionUtil.getConnection(),"update account_spring set money = ? where name = ?",account.getMoney(),account.getName());
} catch (SQLException e) {
e.printStackTrace();
}
}
Joinpoint(连接点 ):就是业务层中的所有方法/接口中的方法
Pointcut(切入点 ):就是动态代理是通过方法名筛选方法
Advice(通知/增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。 通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
//整个Invoke方法就是环绕通知 环绕通知是有切入点方法调用
public Object Invoke(){
try{
txManager.begintran()//前置通知
rt = method.invoke()
txManager.commit //后置通知
}catch(Exception e){
txManager.rollback()//异常通知
}finally{
txManager.release()//最终通知
}
}
Introduction(引介 ):
引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方
法或 Field
Target(目标对象 ):代理的目标对象。//(UserService)
Weaving(织入 ): 是指把增强应用到目标对象来创建新的代理对象的过程。//return 新的proxyService
spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
Proxy(代理) :
一个类被 AOP 织入增强后,就产生一个结果代理类。//return 新的proxyService
Aspect(切面 ):是切入点和通知(引介)的结合。//那些方法被增强过
<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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">bean>
<bean id="logger" class="com.itheima.utils.Logger">bean>
<aop:config>
<aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"> aop:pointcut>
<aop:aspect id="logAdvice" ref="logger">
<aop:around method="aroundPringLog" pointcut-ref="pt1">aop:around>
aop:aspect>
aop:config>
beans>
package com.itheima.utils;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 用于记录日志的工具类,它里面提供了公共的代码
*/
public class Logger {
/**
* 前置通知
*/
public void beforePrintLog(){
System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了。。。");
}
/**
* 后置通知
*/
public void afterReturningPrintLog(){
System.out.println("后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。");
}
/**
* 异常通知
*/
public void afterThrowingPrintLog(){
System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。。。");
}
/**
* 最终通知
*/
public void afterPrintLog(){
System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。");
}
/**
* 环绕通知
* 问题:
* 当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了。
* 分析:
* 通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
* 解决:
* Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
* 该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
*
* spring中的环绕通知:
* 它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
*/
public Object aroundPringLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try{
Object[] args = pjp.getArgs();//得到方法执行所需的参数
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");
rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");
return rtValue;
}catch (Throwable t){
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
throw new RuntimeException(t);
}finally {
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
}
}
}
log类
package com.itheima.utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 用于记录日志的工具类,它里面提供了公共的代码
*/
@Component("logger")
@Aspect//表示当前类是一个切面类
public class Logger {
@Pointcut("execution(* com.itheima.service.impl.*.*(..))")
private void pt1(){}
/**
* 前置通知
*/
// @Before("pt1()")
public void beforePrintLog(){
System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了。。。");
}
/**
* 后置通知
*/
// @AfterReturning("pt1()")
public void afterReturningPrintLog(){
System.out.println("后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。");
}
/**
* 异常通知
*/
// @AfterThrowing("pt1()")
public void afterThrowingPrintLog(){
System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。。。");
}
/**
* 最终通知
*/
// @After("pt1()")
public void afterPrintLog(){
System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。");
}
/**
* 环绕通知
* 问题:
* 当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了。
* 分析:
* 通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
* 解决:
* Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
* 该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
*
* spring中的环绕通知:
* 它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
*/
@Around("pt1()")
public Object aroundPringLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try{
Object[] args = pjp.getArgs();//得到方法执行所需的参数
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");
rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");
return rtValue;
}catch (Throwable t){
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
throw new RuntimeException(t);
}finally {
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
}
}
}
开启注解支持
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
<aop:config>
<aop:pointcut id="pt1" expression="execution(* com.hpy.service.Impl.*.*(..))">aop:pointcut>
<aop:aspect id="tranManager" ref="tranManager">
<aop:before method="beginTran" pointcut-ref="pt1"/>
<aop:after-returning method="commit" pointcut-ref="pt1"/>
<aop:after-throwing method="rollback" pointcut-ref="pt1"/>
<aop:after method="release" pointcut-ref="pt1"/>
aop:aspect>
aop:config>
//由于 @After 和 @AfterReturning 执行顺序问题 所以使用@Around
package com.hpy.util;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* 事务管理相关的工具类
* 包含了开启 提交 回滚 释放连接 事务
* Aspect表示是一个切面类
*/
@Component("tranManager")
@Aspect
public class TranManager {
@Resource(name = "connectionUtil")
private ConnectionUtil connectionUtil;
//添加表达式
@Pointcut("execution(* com.hpy.service.Impl.*.*(..))")
public void pt1(){}
/**
* 开始事务
*/
public void beginTran() {
Connection connection = connectionUtil.getConnection();
try {
System.out.println("开启连接");
connection.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 提交事务
*/
public void commit(){
Connection connection = connectionUtil.getConnection();
try {
System.out.println("提交");
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 回滚事务
*/
public void rollback(){
Connection connection = connectionUtil.getConnection();
try {
System.out.println("异常");
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 释放资源
*/
public void release(){
Connection connection = connectionUtil.getConnection();
try {
System.out.println("释放资源");
connection.close();
connectionUtil.removeConnection();
} catch (SQLException e) {
e.printStackTrace();
}
}
@Around("pt1()")//注意:千万别忘了写括 号
public Object transactionAround(ProceedingJoinPoint pjp) {
//定义返回值
Object rtValue = null;
try {
//获取方法执行所需的参数
Object[] args = pjp.getArgs(); //前置通知:开启事务
beginTran();
//执行方法
rtValue = pjp.proceed(args); //后置通知:提交事务
commit();
}catch(Throwable e) {
//异常通知:回滚事务 rollback(); e.printStackTrace();
rollback();
}finally { //最终通知:释放资源 release();
release();
}
return rtValue; }
}
注解:
try{
@Before
方法
@Around
}catch(){
@AfterThrowing
}finally{
@After
}
@AfterReturning
xml解析:
try{
before
方法
after-returning
}catch(){
after-throwing
}finally{
after
}
@Configuration
@ComponentScan(basePackages="com.itheima")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}
<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.xsd">
<bean id="accountDao" class="com.hpy.dao.Impl.AccountDaoImpl">
<property name="dataSource" ref="ds"/>
bean>
<bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybaitis"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
bean>
beans>
package com.hpy.dao.Impl;
import com.hpy.dao.AccountDao;
import com.hpy.domain.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import java.util.List;
/**
* 继承JdbcDaoSupport 这里实现了 无template时创建template 所以只需要dataSource即可 bean.xml中只需要一个bean
* 使用 getTemplate() 获取 template 对象(xml配置使用这种) 注解还是以前那种方式
*/
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
public List<Account> findAll() {
String sql = "select * from account_spring";
return getJdbcTemplate().query(sql,new BeanPropertyRowMapper<Account>(Account.class));
}
public Account findById(int id) {
Account account = null;
try {
String sql = "select * from account_spring where id = ?";
account = getJdbcTemplate().queryForObject(sql,Account.class,id);
}catch (Exception e){
}
return account;
}
public Account findByName(String name) {
Account account = null;
try {
String sql = "select * from account_spring where name = ?";
account = getJdbcTemplate().queryForObject(sql,Account.class,name);
}catch (Exception e){
}
return account;
}
public void delete(int id) {
String sql = "delete from account_spring where id = ?";
getJdbcTemplate().update(sql,id);
}
}
<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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="accountService" class="com.hpy.service.Impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
bean>
<bean id="accountDao" class="com.hpy.dao.Impl.AccountDaoImpl">
<property name="dataSource" ref="ds"/>
bean>
<bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybaitis"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="ds"/>
bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer"/>
<tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="pt" expression="execution(* com.hpy.service.Impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
aop:config>
beans>
@Service("accountService")
@Transactional(propagation = Propagation.SUPPORTS ,readOnly = true)//只读 查询
public class AccountServiceImpl implements AccountService {
@Resource(name = "accountDao")
private AccountDao accountDao;
public Account findAccountByID(int id) {
return accountDao.findById(id);
}
@Transactional(propagation = Propagation.REQUIRED ,readOnly = false)
public void transfer(String sourceName, String targetName, Float money) {
Account source = accountDao.findByName(sourceName);
Account target = accountDao.findByName(targetName);
source.setMoney(source.getMoney() - money);
target.setMoney(target.getMoney() + money);
accountDao.update(source);
//int i = 1/0;
accountDao.update(target);
}
}
<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"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.hpy"/>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds"/>
bean>
<bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybaitis"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="ds"/>
bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
beans>