在传统的项目中,一般遵循MVC开发模型。
(1) view层与用户进行交互,显示数据或者将数据传输给view层。
(2) 在controller层创建service层对象,调用service层中业务方法。
(3) 在service层创建dao层对象,调用dao层中操作数据的方法。
(4) dao层进行具体的数据库操作
程序在一定程度上存在耦合性,不利于程序的扩展。在controller中直接创建了service层类的对象。如果service的业务发生了变更,那么就需要修改controller层的代码。
在进行程序的扩展时,不建议在当前程序的基础上直接修改程序,为了保证之前程序的正常,一般遵循开闭原则进行维护性的修改,对扩展开放,对修改关闭。
例如:用户注册功能。用户注册,从账号、密码、手机号等信息即可。随着行业发展,目前要求进行实名认证。
1.已注册未认证用户登录时进行认证。
2.新用户注册后要求进行认证。
为避免对已有业务的改动,可以新建一个service类,重写注册方法。则在controller层需要创建新的service对象。每个相关的controller层的代码都需要改动,并且每个controller都需要创建对象,存在对象的浪费。
面向过程—>面向对象---->面向接口---->面向切面(组件)
ArrayList aList = new ArrayList();
List aList = new LinkedList()
基于每个controller都要修改service的创建问题,可以为service定义一个统一的创建方式,例如对象工厂模式,使用工厂创建对象,这样以后使用工厂创建对象的对象需要维护时,只需要修改对象工厂即可,且可以结合单例模式,对象工厂返回单例,这样优化了对象重复的浪费问题。
虽然单例工厂能够解决对象的维护和重复问题。但是,随着service层的扩大,工厂也逐渐臃肿,基本每个service会对应一个factory。基于这种情况,则又需要解决工厂臃肿的问题,为此可以利用反射技术,反射可以创建任意类的对象。但是,工厂为保证单例,只能存储的对象只有一个,而controller层需要使用不同的service层对象,为保证对象的有效,且反射性能相对较低,基于这样的情况,则可以定义一个需要创建的对象的清单和一个存储对象的容器,根据清单创建对象,然后将所有创建service对象进行存储,每个controller只需要去容器中获取对象即可,这样既解决了单例问题,也提高了性能。
1.3.3 对象容器工厂
package org.springframework.context;
public interface ApplicationContext {
/**
* 获取spring容器创建好的对象
* @param name
* @return
*/
Object getBean(String name);
/**
* 获取spring容器创建好的对象
* @param name
* @return
*/
<T> T getBean(String name,Class<T> c);
}
package org.springframework.context.support;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.context.ApplicationContext;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* spring的核心类
*/
public class ClassPathXmlApplicationContext implements ApplicationContext {
/**
* 用于存放id或者name与某个类对象的映射关系
*/
private Map<String,Object> objMap = new HashMap<>();
public ClassPathXmlApplicationContext(){}
public ClassPathXmlApplicationContext(String xmlPath){
try {
//将spring-context.xml中配置的bean对应的类的对象默认全部创建好
SAXReader reader = new SAXReader();
InputStream ins = ClassPathXmlApplicationContext.class.getClassLoader().getResourceAsStream(xmlPath);
Document doc = reader.read(ins);
//读取xml的根标签
Element rootElement = doc.getRootElement();
//拿到beans根标签下的所有bean子标签
List<Element> childEleList = rootElement.elements();
for(Element temp : childEleList){
//读取某个bean标签的id、name、class的属性值
String id = temp.attributeValue("id");
String name = temp.attributeValue("name");
String cls = temp.attributeValue("class");
Object obj = Class.forName(cls).newInstance();
//以id做key,拿到class的全路径创建对象作为value
//将键值对存储到objMap中
objMap.put(id,obj);
//拿到name以,切割 以切割多个字符串做key,拿到class的全路径创建对象作为value
String[] nameAtt = name.split(",");
for(String strName: nameAtt){
objMap.put(strName,obj);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取spring容器创建好的对象
* @param name
* @return
*/
@Override
public Object getBean(String name) {
return objMap.get(name);
}
/**
* 获取spring容器创建好的对象
* @param name
* @return
*/
@Override
public <T> T getBean(String name, Class<T> c) {
return (T)objMap.get(name);
}
}
1.3.4 程序相关类
1.3.4.1 test
package com.powernode.test;
import com.powernode.domain.Dog;
import com.powernode.domain.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SelfSpringTest {
public static void main(String[] args) {
//启动spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
User u1 = context.getBean("u1", User.class);
u1.sleep();
}
}
Spring框架是由于软件开发的复杂性而创建的,初衷是为了让软件开发更加简单。Spring使用的是简单的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
Web Service,有2个显著特点:
1.数据格式是xml格式。
2.配置繁琐,“笨重”,对象关联性大,需在配置文件中各种配置。
基于这些原因,Spring框架提出了:IOC/DI(控制反转/依赖注入),AOP(面向切面编程)。
Spring框架可在任何类型的部署平台上为基于Java的现代企业应用程序提供全面的编程和配置模型。Spring的关键元素是在应用程序级别的基础架构支持:Spring专注于企业应用程序的“管道”,以便团队可以专注于应用程序级别的业务逻辑,而不必与特定的部署环境建立不必要的联系。
2.2.1 核心容器
核心容器由 spring-core,spring-beans,spring-context,spring-context-support和spring-expression(SpEL,Spring 表达式语言,Spring Expression Language)等模块组成,它们的细节如下:
l spring-core 模块提供了框架的基本组成部分,包括 IoC 和依赖注入功能。
l spring-beans 模块提供 BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦。
l context 模块建立在由 core和 beans 模块的基础上建立起来的,它以一种类似于 JNDI 注册的方式访问对象。Context 模块继承自 Bean 模块,并且添加了国际化(比如,使用资源束)、事件传播、资源加载和透明地创建上下文(比如,通过 Servelet 容器)等功能。Context 模块也支持 Java EE 的功能,比如 EJB、JMX 和远程调用等。ApplicationContext 接口是 Context 模块的焦点。spring-context-support 提供了对第三方库集成到 Spring 上下文的支持,比如缓存(EhCache, Guava, JCache)、邮件(JavaMail)、调度(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)等。
l spring-expression 模块提供了强大的表达式语言,用于在运行时查询和操作对象图。它是 JSP2.1 规范中定义的统一表达式语言的扩展,支持 set 和 get 属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从 Spring IoC 容器检索对象,还支持列表的投影、选择以及聚合等。
2.2.2 数据访问/集成
数据访问/集成层包括 JDBC,ORM,OXM,JMS 和事务处理模块,它们的细节如下:
(注:JDBC=Java Data Base Connectivity,ORM=Object Relational Mapping,OXM=Object XML Mapping,JMS=Java Message Service)
l JDBC 模块提供了 JDBC 抽象层,它消除了冗长的 JDBC 编码和对数据库供应商特定错误代码的解析。
l ORM 模块提供了对流行的对象关系映射 API 的集成,包括 JPA、JDO 和 Hibernate 等。通过此模块可以让这些 ORM 框架和 spring的其它功能整合,比如前面提及的事务管理。
l OXM 模块提供了对 OXM 实现的支持,比如 JAXB、Castor、XML Beans、JiBX、XStream 等。
l JMS 模块包含生产(produce)和消费(consume)消息的功能。从 Spring 4.1 开始,集成了 spring-messaging 模块。
l 事务模块为实现特殊接口类及所有的 POJO 支持编程式和声明式事务管理。(注:编程式事务需要自己写 beginTransaction()、commit()、rollback() 等事务管理方法,声明式事务是通过注解或配置由 spring 自动处理,编程式事务粒度更细)
2.2.3 Web
Web 层由 Web,Web-MVC,Web-Socket 和 Web-Portlet 组成,它们的细节如下:
l Web 模块提供面向 web 的基本功能和面向 web 的应用上下文,比如多部分(multipart)文件上传功能、使用 Servlet 监听器初始化 IoC 容器等。它还包括 HTTP 客户端以及 Spring 远程调用中与 web 相关的部分。
l Web-MVC 模块为 web 应用提供了模型视图控制(MVC)和 REST Web服务的实现。Spring 的 MVC 框架可以使领域模型代码和 web 表单完全地分离,且可以与 Spring 框架的其它所有功能进行集成。
l Web-Socket 模块为 WebSocket-based 提供了支持,而且在 web 应用程序中提供了客户端和服务器端之间通信的两种方式。
l Web-Portlet 模块提供了用于 Portlet 环境的 MVC 实现,并反映了 spring-webmvc 模块的功能。
2.2.4 其他
还有其他一些重要的模块,像 AOP,Aspects,Instrumentation,Web 和测试模块,它们的细节如下:
l AOP 模块提供了面向方面(切面)的编程实现,允许你定义方法拦截器和切入点对代码进行干净地解耦,从而使实现功能的代码彻底的解耦出来。使用源码级的元数据,可以用类似于.Net属性的方式合并行为信息到代码中。
l Aspects 模块提供了与 AspectJ 的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架。
l Instrumentation 模块在一定的应用服务器中提供了类 instrumentation 的支持和类加载器的实现。
l Messaging 模块为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。
l 测试模块支持对具有 JUnit 或 TestNG 框架的 Spring 组件的测试。
IOC:控制反转。将对象的创建、初始化、销毁等一系列的生命周期过程交给spring管理。
结婚:
方式1: 自己找女生--------------->吃饭、逛街、看电影、送回家等--------->结婚
(同学、同事、公交地铁1个月-3个月) 半年-1年半 1天
方式2: 媒婆(1个月)------------------------>结婚(1天)
吃饭:
方式1: 买菜、买米(30min-1h)---->蒸饭、洗菜、切菜、炒菜(1个小时)—>吃(15-30min)
方式2: 定外卖----------------->吃(15-30min)
面向过程----->面向对象----->面向接口----->面向组件(面向切面)–>面向服务—>面向百度
在pom文件中,引入Spring的IoC核心jar包
<!--依赖-->
<dependencies>
<!--引入spring的context依赖,可以传递出aop beans core expression-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.4</version>
</dependency>
</dependencies>
3.1.2 创建User类
package com.bjpowernode.domain;
import java.util.Date;
/**
* Created on 2021/7/16
*
* @author 雷哥
*/
public class User {
private Integer id;
private String name;
private Integer age;
private String address;
private Date birth;
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
", birth=" + birth +
'}';
}
}
3.1.3 创建Spring的核心配置文件spring-context.xml/applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- spring 核心配置文件 配置IOC容器中需要创建的bean -->
<bean id="userId" name="user" class="com.bjpowernode.domain.User" />
</beans>
3.1.4 编写测试程序
public class Test {
public static void main(String[] args) {
//根据 spring的配置文件 创建 应用容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
System.out.println("马上获取bean");
//从容器中获取 对象
User user = (User) context.getBean("user");
System.out.println(user);
}
}
3.2.1 id
是 bean的唯一标识 一个bean,其id 值只能有一个 。整个IOC 容器id 值不允许重复,使用名称作为key。
3.2.2 name
一个bean的名称,可以存在多个,多个之间使用逗号分隔。不论bean有没有定义name属性,默认id都会当做name。
3.2.3 class
bean的具体的类型,包名和类名组成。
3.2.4 scope
bean的作用域:如果不写scope,则默认为单例
prototype :非单例,每次获取都会创建一个新的bean对象。
singleton : 单例,多次获取永远同一个bean, 默认值。
request : 一次请求,基于web项目的bean的作用域。
session : 一次会话,基于web项目的bean的作用域。
3.2.5 lazy-init
延迟初始化(懒加载),默认只要加载了配置文件。bean对象就会被初始化,lazy-init则是获取时才会初始化。只针对单例模式有效,非单例每次获取都会创建,没有延迟初始化的意义
3.2.6 depends-on
初始化时依赖的对象,当前对象初始化前需先初始化depends-on指定的对象
3.2.7 init-method
对象初始化后,调用的方法
3.2.8 destroy-method
对象销毁时,调用的方法
3.2.9 autowire
属性自动装配
byName 根据属性名称装配
byType 根据类型装配
3.2.10 autowire-candidate
是否允许作为自动装配的候选项
true 作为自动装配的候选项
false 不作为自动装配的候选项
3.2.11 primary
优先使用该bean,因为Spring需要支持使用类型查找对象,在一个大类型下,可能存在多个小类型。如果根据大类型装配属性时,不知道使用哪个具体的对象,则可以根据primary设置优先级。
3.2.12 代码示例
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- spring 核心配置文件 配置IOC容器中需要创建的bean -->
<!-- <bean id="userId" name="user" class="com.bjpowernode.domain.User" />-->
<!--
id : bean的唯一标识 整个IOC容器不能重复。
name : bean的key,多个name之间使用逗号,
class : 具体的bean的全路径
scope : bean的作用域
singleton 单例 默认
prototype 非单例
lazy-init="true" 获取时创建对象
depends-on="test" 默认自上而下创建 depends-on 会优先创建 depends-on 对应的bean
init-method : 对象创建后调用的方法
destroy-method :对象销毁时调用的方法, 容器调用close
autowire : 属性自动装配
byName 根据属性名装配
byType 根据属性类型装配
primary : 当存在多个同样的类型时, primary 为true 则优先使用该bean
-->
<bean id="userId2" name="user1,user2" class="com.bjpowernode.domain.User" scope="singleton" depends-on="test" init-method="init" destroy-method="destory" primary="true" />
<!-- test类 -->
<bean name="test" class="com.bjpowernode.domain.Test" />
<bean name="userService1" class="com.bjpowernode.service.impl.UserServiceImpl1" primary="true" />
<bean name="userService2" class="com.bjpowernode.service.impl.UserServiceImpl2" />
</beans>
package com.bjpowernode.service;
public interface IUserService {
}
package com.bjpowernode.service.impl;
import com.bjpowernode.service.IUserService;
/**
* @Description: 接口实现类1
* @author: Mr.T
* @date 2020-09-26 14:33
*/
public class UserServiceImpl1 implements IUserService {
}
package com.bjpowernode.service.impl;
import com.bjpowernode.service.IUserService;
public class UserServiceImpl2 implements IUserService {
}
package com.bjpowernode.domain;
public class User {
private Integer id;
private String name;
public User(){
System.out.println("构造方法执行 user 对象进行创建");
}
public void sleep(){
System.out.println("早睡早起!!!");
}
public void init(){
System.out.println("对象初始化后调用的方法");
}
public void destory(){
System.out.println("对象销毁时调用");
}
}
package com.bjpowernode.test;
import com.bjpowernode.domain.User;
import com.bjpowernode.service.IUserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
//根据 spring的配置文件 创建 应用容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
System.out.println("马上获取bean");
//从容器中获取 对象
// 装备对象 : XX XXX1 XX2 xx3
User user = (User) context.getBean("user2");//
System.out.println("21:"+user);
System.out.println("22:"+context.getBean("user2"));
//获取接口的类型
System.out.println(context.getBean(IUserService.class));
context.close();//关闭容器 此时会调用销毁的方法
}
}
3.3.1 构造方法创建
使用构造方法创建bean对象,是spring默认的创建方式。
<!-- 使用构造方法创建对象 -->
<bean id="user" class="com.bjpowernode.domian.User" />
3.3.2 静态工厂创建
<!-- 使用静态工厂创建对象 -->
<!--
id : bean 的唯一标识
class : 工厂类
factory-method : 工厂方法
-->
<bean id="user2" class="com.bjpowernode.factory.UserStaticFactory" factory-method="getObj" />
package com.bjpowernode.factory;
import com.bjpowernode.domian.User;
public class UserStaticFactory {
/**
* 静态工厂中用于创建对象的方法
* @return
*/
public static User getObj(){
System.out.println("静态工厂中 创建对象的方法 执行了");
return new User();
}
}
3.3.3 非静态工厂创建
<!--
非静态工厂创建对象
在非静态工厂中,创建对象的方法是非静态方法。非静态方法的执行,首先需要该类对象
注意: 使用非静态工厂创建对象,首先需要创建工厂类对象
-->
<!-- 工厂类对象 -->
<bean id="userFactory" class="com.bjpowernode.factory.UserFactory" />
<!-- 使用非静态工厂创建对象 -->
<!--
factory-bean : 非静态工厂对象
factory-method : 创建对象的非静态方法
-->
<bean id="user3" factory-bean="userFactory" factory-method="getObj" />
package com.bjpowernode.factory;
import com.bjpowernode.domian.User;
public class UserFactory {
/**
* 工厂中用于创建对象的方法
* @return
*/
public User getObj(){
System.out.println("非静态工厂中创建对象的方法 执行了");
return new User();
}
}
3.3.4 注解创建
Spring为简化对象的创建方式,提供了注解。
3.3.4.1 组件注解:
3.3.4.1.1 @Component(“bs”)
表示该类为一个被Spring管理的组件。但是,由于在开发中为了让代码的可读性更高。
Spring基于分层思想,将需要创建的组件分为以下几类:
3.3.4.1.2 @Controller
@Controller注解,标识该类是controller层的类。并且,注意在使用SpringMVC时,所有的Constroller,必须使用@Controller注解。
3.3.4.1.3 @Service
@Service注解,标识该类是业务层的类。
3.3.4.1.4 @Respository
@Respository注解,标识该类是操作数据层的类。
注意:
以上注解是Spring中定义的创建对象的注解,都可以创建对象,如果该类有明确的作用,有自己所属的层,则建议使用相应的注解,如果实在无法区分该类所属层,可以使用@Component注解。
3.3.4.2 注解使用步骤
3.3.4.2.1 开启组件扫描
在spring的核心配置文件中,开启注解扫描,让Spring将被注解修饰的类,创建对相关。
xml头部
xmlns:context=“http://www.springframework.org/schema/context”
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
<!--
开启组件扫描
-->
<context:component-scan base-package="com.bjpowernode.*" />
3.3.4.2.2 添加注解
package com.bjpowernode.domian;
import org.springframework.stereotype.Component;
@Component
public class Person {
public Person(){
System.out.println("Person的构造方法.........");
}
}
3.3.5 什么时候使用XML配置和注解
能使用注解时,就使用了注解,注解非常方便。但是,在第三方的类中,是无法使用注解的。因为无法在别人提供的源码上加上Spring注解,此时只能使用XML配置的形式,配置第三方类的Bean信息。
为对象属性设置值,就是属性注入。
3.4.1 构造方法属性注入:DI(依赖注入,给对象的属性赋值)
<?xml version="1.0" encoding="UTF-8"?>
<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 class="com.bjpowernode.domain.User" />
<!--
有参数的构造方法
此时是2个参数的构造方法
index : 参数下标 从 0开始
value : 属性的值
-->
<bean id="user2" class="com.bjpowernode.domain.User" >
<constructor-arg index="0" value="1001" />
<constructor-arg index="1" value="韩梅梅" />
</bean>
<!-- 有参数的构造方法
使用index下标查找 属性 存在问题 都只有一个参数 则默认使用后面的构造方法
可以使用 type 指定参数的类型
更推荐 使用name属性 : name表示构造器中参数的名称
-->
<bean id="user3" class="com.bjpowernode.domain.User">
<constructor-arg index="0" value="1001" type="java.lang.Integer" />
</bean>
<bean id="user4" class="com.bjpowernode.domain.User">
<constructor-arg name="name" value="韩梅梅" />
</bean>
</beans>
package com.bjpowernode.domain;
public class User {
/**
* id 属性
*/
private Integer id;
/**
* name 属性
*/
private String name;
public User(){
System.out.println("无参数构造方法");
}
public User(Integer id) {
System.out.println("id参数方法");
this.id = id;
}
public User(String name) {
System.out.println("name参数构造方法");
this.name = name;
}
public User(Integer id, String name) {
System.out.println("id,name参数构造方法");
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
3.4.2 set方法属性注入
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!--
使用set方法进行属性注入
property 表示属性
name : 表示属性对应的set方法名,去掉set前缀,首字母小写。并不是真正的属性名
-->
<bean id="dg" class="com.powernode.pojo.Dog">
<property name="sonList">
<list>
<value>小黑</value>
<value>小白</value>
<value>小花</value>
</list>
</property>
<property name="wifeList">
<set>
<value>小翠儿</value>
<value>如花</value>
</set>
</property>
<property name="friendMap">
<map>
<entry key="uName1" value="土狗"></entry>
<entry key="uName2" value="泰迪"></entry>
<entry key="uName3" value-ref="u1"></entry>
</map>
</property>
<property name="props">
<props>
<prop key="strain">土狗</prop>
<prop key="age">15</prop>
<prop key="gender">公</prop>
</props>
</property>
</bean>
</beans>
3.4.3 注解属性注入
在spring中,为了简化属性的注入,Spring提供注解:@Autowired,Spring会自动从IOC容器中,为这个属性查找相应类型的值,进行注入。
开启包的注解扫描
使用注解
package com.bjpowernode.domain;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Date;
public class Student {
public Integer id;
public String name;
@Autowired //使用注解自动注入
//User 对象
public User user;
public void setStudentId(Integer id) {
System.out.println("set方法被调用了............");
this.id = id;
}
/* public void setUser(User user) {
this.user = user;
}*/
}
注意:
在使用自动注入时,可以在bean标签上,配置autowire,但是此时必须有该属性的set方法,@Autowired注解,是不需要set方法的。
如果是在xml中注入对象,值使用ref属性。value属性,只支持boolean,数字,字符串等。
在Spring中,提供了丰富的标签,进行各种属性的注入。常见的类型:
数字、字符串、boolean、数组、set、list、map、properties。
package com.bjpowernode.domain;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class Person {
private Integer id;
private String name;
private boolean sex;// true 男 false 女
private String[] likes;//爱好
private Set<String> girlFriends; //女朋友
private List<Dream> dreams;//梦想
private Map<String,String> house; //房子
private Properties properties; //配置文件属性
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean getSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
public String[] getLikes() {
return likes;
}
public void setLikes(String[] likes) {
this.likes = likes;
}
public Set<String> getGirlFriends() {
return girlFriends;
}
public void setGirlFriends(Set<String> girlFriends) {
this.girlFriends = girlFriends;
}
public List<Dream> getDreams() {
return dreams;
}
public void setDreams(List<Dream> dreams) {
this.dreams = dreams;
}
public Map<String, String> getHouse() {
return house;
}
public void setHouse(Map<String, String> house) {
this.house = house;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
package com.bjpowernode.domain;
public class Dream {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
<?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: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 https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 简单的属性注入 : 基本数据类型 + string -->
<bean id="person1" class="com.bjpowernode.domain.Person" >
<!-- Integer 类型的属性注入 -->
<property name="id" value="1001" />
<!-- String 类型的属性注入 -->
<property name="name" value="佚名" />
<!-- boolean 属性注入 -->
<property name="sex" value="true" />
</bean>
<!-- 数组类型 -->
<bean id="person2" class="com.bjpowernode.domain.Person" >
<property name="likes" >
<array>
<value>足球</value>
<value>篮球</value>
<value>羽毛球</value>
</array>
</property>
</bean>
<!-- set 类型注入 -->
<bean id="person3" class="com.bjpowernode.domain.Person" >
<property name="girlFriends" >
<set>
<value>韩梅梅</value>
<value>Lucy</value>
<value>Lucy</value>
<value>Rose</value>
</set>
</property>
</bean>
<bean id="dream1" class="com.bjpowernode.domain.Dream">
<property name="title" value="数钱数到手抽筋" />
</bean>
<!-- list 类型 -->
<bean id="person4" class="com.bjpowernode.domain.Person" >
<property name="dreams" >
<list>
<bean class="com.bjpowernode.domain.Dream">
<property name="title" value="解放全人类" />
</bean>
<bean class="com.bjpowernode.domain.Dream">
<property name="title" value="世界和平" />
</bean>
<ref bean="dream1"/>
</list>
</property>
</bean>
<!-- map结构 -->
<bean id="person5" class="com.bjpowernode.domain.Person" >
<property name="house" >
<map>
<entry key="wh" value="江滩" />
<entry key="bj" value="后海" />
<entry key="hz" value="西湖" />
</map>
</property>
</bean>
<!-- properties -->
<bean id="person6" class="com.bjpowernode.domain.Person" >
<property name="properties" >
<props>
<prop key="driver">驱动</prop>
<prop key="url">url</prop>
</props>
</property>
</bean>
</beans>
package com.bjpowernode.test;
import com.bjpowernode.domain.Dream;
import com.bjpowernode.domain.Person;
import com.bjpowernode.domain.Student;
import com.bjpowernode.domain.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
Person person1 = (Person) context.getBean("person1");
System.out.println(person1.getId() +" "+ person1.getName() + " "+ person1.getSex() );
Person person2 = (Person) context.getBean("person2");
String[] likes = person2.getLikes();
System.out.println(Arrays.asList(likes));
Person person3= (Person) context.getBean("person3");
System.out.println(person3.getGirlFriends());
Person person4 = (Person) context.getBean("person4");
List<Dream> dreams = person4.getDreams();
System.out.println(dreams.get(0).getTitle());
System.out.println(dreams.get(1).getTitle());
System.out.println(dreams.get(2).getTitle());
Person person5 = (Person) context.getBean("person5");
System.out.println(person5.getHouse());
Person person6 = (Person) context.getBean("person6");
Properties properties = person6.getProperties();
System.out.println(properties.getProperty("driver"));
System.out.println(properties.getProperty("url"));
}
}
3.6.1 xml配置版
3.6.1.1 创建项目并加入依赖
<!--依赖-->
<dependencies>
<!--引入spring的context依赖,可以传递出aop beans core expression-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.4</version>
</dependency>
</dependencies>
3.6.1.3 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- 声明 com.bjpowernode.dao 对象 -->
<bean id="userDao" class="com.bjpowernode.dao.UserDao" />
<!-- 声明service 对象 -->
<bean id="userService" class="com.bjpowernode.service.impl.UserServiceImpl" autowire="byType">
<!-- 使用自动装配 则不需要 声明属性注入 但是要有set方法 -->
<!--<property name="userDao" ref="userDao" />-->
</bean>
<!-- 声明controller -->
<bean id="userController" class="com.bjpowernode.controller.UserController" autowire="byType">
<!-- 使用自动装配 则不需要 声明属性注入 但是要有set方法-->
<!--<property name="userService" ref="userService"/>-->
</bean>
</beans>
3.6.1.4 controller
package com.bjpowernode.controller;
import com.bjpowernode.service.IUserService;
public class UserController {
private IUserService userService;
public void register(){
System.out.println("控制层的 register 方法");
userService.register();
}
public void setUserService(IUserService userService) {
this.userService = userService;
}
}
3.6.1.5 dao
package com.bjpowernode.dao;
public class UserDao {
public void add(){
System.out.println("进行数据库数据新增操作");
}
}
3.6.1.6 service
package com.bjpowernode.service;
public interface IUserService {
/**
* 注册接口
*/
public void register();
}
package com.bjpowernode.service.impl;
import com.bjpowernode.dao.UserDao;
import com.bjpowernode.service.IUserService;
public class UserServiceImpl implements IUserService {
/**
* com.bjpowernode.dao 层 属性
*/
private UserDao userDao;
@Override
public void register() {
System.out.println("com.bjpowernode.service 的 register ");
userDao.add();
}
/**
* 使用set方法 是为了自动装配 或者进行set 属性设置
* @param userDao
*/
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
3.6.1.7 test
package com.bjpowernode;
import com.bjpowernode.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
UserController bean = context.getBean(UserController.class);
bean.register();
}
}
3.6.2 注解版
3.6.2.1 配置文件
<?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: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 https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启组件扫描 -->
<context:component-scan base-package="com.*" />
</beans>
3.6.2.2 controller
package com.bjpowernode.controller;
import com.bjpowernode.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
@Autowired
private IUserService userService;
public void register(){
System.out.println("控制层的 register 方法");
userService.register();
}
}
3.6.2.3 dao
package com.bjpowernode.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
public void add(){
System.out.println("进行数据库数据新增操作");
}
}
3.6.2.4 service
package com.bjpowernode.service;
public interface IUserService {
/**
* 注册接口
*/
public void register();
}
package com.bjpowernode.service.impl;
import com.bjpowernode.dao.UserDao;
import com.bjpowernode.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements IUserService {
/**
* com.bjpowernode.dao 层 属性
*/
@Autowired
private UserDao userDao;
@Override
public void register() {
System.out.println("com.bjpowernode.service 的 register ");
userDao.add();
}
}
3.6.2.5 test
package com.bjpowernode.test;
import com.bjpowernode.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
//UserController bean = context.getBean(UserController.class);
//bean.register();
//程序员自己创建的对象 不在IOC 自己容器中
//new UserController().register();
}
}
com.powernode.aadenglu
Login: login():登录方法
com.powernode.anquan
Security: isSecurity():检测操作环境是否安全
com.powernode.bmapper
CBCBankMapper(核心类): selectMoney() updateMoney() updateInvest() update2Tel()
com.powernode.crizhi
Logger: log():记录用户操作细节
com.powernode.dqinli
Clean :cleanResource():清理缓存
Expt: handExpt()
DefaultAopProxyFactory
代理
静态代理
静态代理,每个被代理类都需要创建对应的代理类。随着程序的扩展,代理类也会增多,臃肿,维护量变多,为了解决这个问题,Java中,提供了动态代理技术,开发者不需要自己定义代理类,代理类由JDK动态的创建,开发只需要指定被代理的类即可。
切面(aspect):除了核心类以外的其他类称之为切面
通知(advisor):切面中的方法称之为通知
核心类:一个项目中不能省略的模块类
核心方法:核心类中的方法称之为核心方法
连接点:核心类中的某一个方法
切入点(pointcut):某个包下的某个类下的某一批方法
代理(proxy):将多个切面与核心类组合在一起,形成一个新的类,这个类称之为代理类
织入(weaver):书写代理类的过程称之为织入
4.2.1 JDK动态代理
4.2.1.1 Proxy
该类提供了方法创建代理类和代理类的对象的方法
创建一个代理类并返回代理类对象
static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)
loader : 类加载器,指定类加载器,是为了精确的定位类
interfaces : 接口Class类,使用JDK的反射,必须要有接口
h :InvocationHandler ,代理的处理器,每个代理类都有一个关联的处理器
4.2.1.2 InvocationHandler
是每个代理类对应的处理器
Object 方法调用的返回值,可以作为被代理的方法调用的返回值
proxy : 代理类对象
method : 目标类中被代理的方法
args : 目标类中被代理的方法的运行参数
Object invoke(Object proxy,Method method,Object[] args)
4.2.1.3 代码示例
4.2.1.3.1 目标类接口
package com.bjpowernode.proxy.demo02;
/**
* @Description: 目标类接口
* @author: Mr.T
* @date 2020-09-27 10:38
*/
public interface ITargetClass {
/**
* 房子出租
* @param m
*/
void rent(int m);
}
4.2.1.3.2 目标类
package com.bjpowernode.proxy.demo02;
/**
* @Description: 目标类
* @author: Mr.T
* @date 2020-09-27 10:39
*/
public class TargetClass implements ITargetClass {
@Override
public void rent(int m) {
System.out.println("出租的金额为:" + m);
}
}
4.2.1.3.3 代理类处理器
package com.bjpowernode.proxy.demo02;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @Description: 代理的处理器
* 处理器会被绑定一个代理
* 帮助代理调用目标方法
* @author: Mr.T
* @date 2020-09-27 10:40
*/
public class ProxyHanlder implements InvocationHandler {
/**
* 目标方法类的对象
*/
private Object targetObj;
public ProxyHanlder(Object targetObj){
this.targetObj = targetObj;
}
/**
*
* @param proxy 生成的代理类的对象
* @param method 目标类中被代理的方法
* @param args 目标类中被代理的方法实际参数
* @return 可以当做目标方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//使用反射调用 目标类中的方法
Object obj = method.invoke(targetObj, args);
return obj;
}
}
4.2.1.3.4 测试类
package com.bjpowernode.proxy.demo02;
import java.lang.reflect.Proxy;
/**
* @Description: TODO
* @author: Mr.T
* @date 2020-09-27 10:45
*/
public class Test {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
//创建了目标类对象
ITargetClass targetClass = new TargetClass();
//创建处理器
ProxyHanlder proxyHanlder = new ProxyHanlder(targetClass);
//创建具体的代理类和对象 具体产生的代理类 会实现 接口 所以能够转化为 接口类型
ITargetClass proxy = (ITargetClass) Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{ITargetClass.class}, proxyHanlder);
// 调用代理类中 rent方法 $proxy0
proxy.rent(100);
}
}
4.2.1.3.5 生成代理类源码
package com.bjpowernode.proxy.demo02;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.UndeclaredThrowableException;
/**
* @Description: 动态代理生成的代理类
* Proxy : JDK中所有生成的代理的父类
* ITargetClass : 指定被代理类的接口
*
* @author: Mr.T
* @date 2020-09-27 10:59
*/
public final class $Proxy0 extends Proxy implements ITargetClass
{
private static Method m0; //hashCode 方法
private static Method m1; // equals
private static Method m2; //toString
private static Method m3; // rent 方法 被代理的方法
static {
try {
$Proxy0.m0 = Class.forName("java.lang.Object").getMethod("hashCode", (Class<?>[])new Class[0]);
$Proxy0.m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
$Proxy0.m2 = Class.forName("java.lang.Object").getMethod("toString", (Class<?>[])new Class[0]);
$Proxy0.m3 = Class.forName("com.bjpowernode.proxy.demo02.ITargetClass").getMethod("rent", Integer.TYPE);
}
catch (NoSuchMethodException ex) {
throw new NoSuchMethodError(ex.getMessage());
}
catch (ClassNotFoundException ex2) {
throw new NoClassDefFoundError(ex2.getMessage());
}
}
/**
* invocationHandler 就是定义的 invocationHandler
* @param invocationHandler
*/
public $Proxy0(final InvocationHandler invocationHandler) {
super(invocationHandler); //Proxy(InvocationHandler invocationHandler) --> 为父类中 InvocationHandler 赋值
//当前类中 InvocationHandler 对象 且有值
/*
Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
* */
}
public final boolean equals(final Object o) {
try {
return (boolean)super.h.invoke(this, $Proxy0.m1, new Object[] { o });
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final String toString() {
try {
return (String)super.h.invoke(this, $Proxy0.m2, null);
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final void rent(final int n) {
try {
//super.h this.h ---> 自定义的 ProxyHanlder
//调用自定义的ProxyHanlder 中的invoke方法 : $Proxy0 对象
//$Proxy0.m3 rent 方法
//new Object[] { n } 传入的参数
super.h.invoke(this, $Proxy0.m3, new Object[] { n });
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final int hashCode() {
try {
return (int)super.h.invoke(this, $Proxy0.m0, null);
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
}
4.2.1.3.6 JDK动态代理的不足
在JDK中使用动态代理,必须有类的接口。因为生成的代理需要实现这个接口,这样我们生成的代理类对象,才能转化为代理目标的接口对象,然后根据接口中的方法,调用处理器中invoke方法。
4.2.2 Cglib动态代理
为了弥补JDK动态代理的不足,第三方组织封装一套工具包,cglib的工具包,这套包不基于接口,基于父子继承,通过重写的形式扩展方法,但是这个子类工具自动生成的。
早期,Cglib动态代理,性能相于JDK的动态代理高一些。JDK进行一些列优化,目前Spring默认使用的动态代理JDK,也支持Cglib。
4.2.2.1 Cglib动态代理的使用
4.2.2.1.1 MethodInterceptor
cglib中,提供的对方法执行拦截的接口。其中intercept是对具体方法进行拦截处理的方法。
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
MethodProxy proxy)
Object : 方法执行返回的结果
obj :增强类的对象
method :目标方法
proxy :用于回调的方法的对象
4.2.2.1.2 代码示例
4.2.2.1.2.1 导入jar包
<!-- 引入cglib 的jar 包-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
4.2.2.1.2.2 创建被代理目标类
package com.bjpowernode.proxy.demo03;
/**
* @Description: 被代理的目标类
* @author: Mr.T
* @date 2020-09-27 14:13
*/
public class TargetClass {
public void rent(){
System.out.println("目标类中的出租方法");
}
}
4.2.2.1.2.3 方法拦截器
package com.bjpowernode.proxy.demo03;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @Description: 方法执行的拦截器
* @author: Mr.T
* @date 2020-09-27 14:20
*/
public class MyMethodInteceptor implements MethodInterceptor {
/**
* 进行具体的拦截的方法
* @param obj 被代理类的对象
* @param method 被代理的目标方法
* @param args 实际运行的参数
* @param proxy 代理类对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("前置增强");
//代理 调用父类中的方法
Object o = proxy.invokeSuper(obj, args);
System.out.println("intercept 执行了");//执行了
System.out.println("后置增强");
return o ;
}
}
4.2.2.1.2.4 测试类
package com.bjpowernode.proxy.demo03;
import net.sf.cglib.proxy.Enhancer;
/**
* @Description: TODO
* @author: Mr.T
* @date 2020-09-27 14:21
*/
public class Test {
public static void main(String[] args) {
//增强类工具 可以创建代理类对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetClass.class);
//设置调用时 回调
enhancer.setCallback(new MyMethodInteceptor());
//创建代理类对象
TargetClass proxy = (TargetClass) enhancer.create();
proxy.rent();
}
}
4.2.2.2 动态代理的不足
不论是JDK的动态代理,还是第三方cglib动态代理,都需要开发者编写代码处理程序。程序结构基本上大同小异,重复造轮子。基于这样的情况,在Spring中,提供了2种方式:xml配置形式和注解形式,使用动态代理。这种模式就是Spring Aop技术。其底层依然是动态代理。
在Spring中,AOP的配置主要分为2类:xml配置和注解配置
XML配置也分为两种,一种Spring的原生支持,一种是Spring的aspects这个相关的框架。
4.3.1 AOP的相关概念
连接点(JoinPoint):所谓连接点是指那些被拦截的点,而spring中这些点就是指方法,因为spring只支持方法类型的连接点。
切入点(PointCut):所谓切入点就是指我们要对那些JoinPoint进行拦截的定义,指的是具体的拦截的位置
增强/通知(Advice) : 增强就是对具体的连接点进行扩展的功能。由于一般对方法进行增强,分为在方法前执行或者方法后,或者发生异常执行等等,所以增强被分为:前置增强(前置通知)、后置增强(后置通知)、环绕通知(环绕增强)、异常增强(异常通知)
引介(Introduction):引介是一种特殊的Advice,在不修改代码的前提下,引介可以在运行期为类动态的添加一些方法或Field.
目标(Target) :被代理的类(需要增强类)
织入(Weaving) :把Advice应用到Target的过程
代理(Proxy):使用AOP配置后产生的代理类
4.3.2 Spring自身AOP具体配置
4.3.2.1 引入aop相关jar包
<?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>org.example</groupId>
<artifactId>02-spring-aop01</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.2.0.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
4.3.2.2 定义增强类
4.3.2.2.1 前置增强-MethodBeforeAdvice
package com.bjpowernode.advice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* @Description: spring 规定的增强方法接口
* @author: Mr.T
* @date 2020-09-27 15:42
*/
public class MyBeforeAdvice implements MethodBeforeAdvice {
/**
*
* @param method 目标方法
* @param args 实际运行的参数
* @param target 目标类对象
* @throws Throwable
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("前置增强的方法");
}
}
4.3.2.2.2 后置增强-AfterReturningAdvice
package com.bjpowernode.advice;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
/**
* @Description: 后置增强接口
* @author: Mr.T
* @date 2020-09-27 15:55
*/
public class MyAfterAdvice implements AfterReturningAdvice {
/**
*
* @param returnValue 被增强的方法运行后返回的数据
* @param method 被增强的方法
* @param args 方法运行的参数
* @param target 目标类对象
* @throws Throwable
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("后置增强方法");
}
}
4.3.2.2.3 环绕增强-MethodInterceptor
package com.bjpowernode.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* @Description: 环绕增强
* @author: Mr.T
* @date 2020-09-27 16:04
*/
public class MyAroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("前置增强");
Object rs = invocation.proceed();//调用 目标方法
System.out.println("后置增强");
return rs;
}
}
4.3.2.2.4 异常增强-ThrowsAdvice
异常增强中的增强方法必须叫:afterThrowing,并且必须定义参数接收发生的异常信息。
package com.bjpowernode.advice;
import org.springframework.aop.ThrowsAdvice;
/**
* @Description: 异常增强类
* @author: Mr.T
* @date 2020-09-27 16:12
*/
public class MyExceptionAdvice implements ThrowsAdvice {
public void afterThrowing(Exception ex){
System.out.println("异常增强的方法!!!!!!");
}
}
4.3.2.3 目标类
package com.bjpowernode;
public interface ITargetClass {
/**
* 待增强的目标方法
*/
void targetMethod();
/**
* 待增强的方法
* @param msg
* @return
*/
String afterTargetMethod(String msg);
/**
* 环绕增强的方法
*/
void aroundTargetMethod();
/**
* 执行会发生异常
*/
void runException();
}
package com.bjpowernode.impl;
import com.bjpowernode.ITargetClass;
/**
* @Description: 目标类
* @author: Mr.T
* @date 2020-09-27 15:38
*/
public class TargetClassImpl implements ITargetClass {
@Override
public void targetMethod() {
System.out.println("待增强的目标方法");
}
@Override
public String afterTargetMethod(String msg) {
System.out.println("待增强的目标方法 --- afterTargetMethod");
return "被增强的方法的返回值";
}
public void aroundTargetMethod() {
System.out.println("待增强的目标方法 --- aroundTargetMethod");
}
/**
* 发生异常
*/
public void runException() {
System.out.println("待增强的目标方法 --- runException");
int m = 0;
int n = 100/m;
}
}
4.3.2.4 aop配置
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 定义目标类对象 -->
<bean id="targetClass" class="com.bjpowernode.impl.TargetClassImpl" />
<!-- 定义增强类对象 前置增强类 -->
<bean id="myBeforeAdvice" class="com.bjpowernode.advice.MyBeforeAdvice" />
<!-- 定义增强类对象 后置增强 -->
<bean id="myAfterAdvice" class="com.bjpowernode.advice.MyAfterAdvice" />
<!-- 定义增强类 环绕增强 -->
<bean id="myAroundAdvice" class="com.bjpowernode.advice.MyAroundAdvice" />
<!-- 定义增强类 异常增强类 -->
<bean id="myExceptionAdvice" class="com.bjpowernode.advice.MyExceptionAdvice" />
<!-- 进行织入 -->
<aop:config>
<!--
id : 连接点的唯一标识
expression : 连接点的表达式
execution(* 包名.类名.方法名(..))
* 指任意字符
.. 表示参数可以是任意个
-->
<aop:pointcut id="beforePoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.targetMethod(..))"/>
<!-- 后置增强的切点 -->
<aop:pointcut id="afterPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.afterTargetMethod(..))"/>
<!-- 环绕增强的切点 -->
<aop:pointcut id="aroundPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.aroundTargetMethod(..))"/>
<!-- 异常增强的切点 -->
<aop:pointcut id="exceptionPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.runException(..))"/>
<!--
织入
将增强和连接点 结合
-->
<aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="beforePoint" />
<!-- 织入
后置增强的织入
-->
<aop:advisor advice-ref="myAfterAdvice" pointcut-ref="afterPoint" />
<!--
织入
环绕增强的织入
-->
<aop:advisor advice-ref="myAroundAdvice" pointcut-ref="aroundPoint" />
<!--
织入
异常增强的织入
-->
<aop:advisor advice-ref="myExceptionAdvice" pointcut-ref="exceptionPoint" />
</aop:config>
</beans>
4.3.2.5 测试类
package com.bjpowernode;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
ITargetClass targetClass = context.getBean(ITargetClass.class);
targetClass.runException();
}
}
4.3.3 AspectJ框架AOP配置
在原生的spring中,每种增强都需要单独定义一个类实现相应的接口。增强类本身就更庞大,而且方法的名称是固定的。基于这种情况,AspectJ提供了相对更加灵活的方式。
在AspectJ中,只需要定义一个增强类即可,并且方法的名称可以任意定义。
4.3.3.1 引入相关jar
<?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>org.example</groupId>
<artifactId>02-spring-aop02</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.2.0.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
4.3.3.2 编写增强类
package com.bjpowernode.advice;
import org.aopalliance.intercept.Invocation;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAdvice {
public void beforAdvice(){
System.out.println("前置增强的方法");
}
public void afterAdvice(String name,String rs){
System.out.println("后置增强的方法");
}
public void aroundAdvice(ProceedingJoinPoint joinPoint){
System.out.println("前置增强");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("后置增强");
}
/**
* 异常增强
* @param exception
*/
public void exceptionAdvice(Exception exception){
System.out.println("异常增强!");
}
}
4.3.3.3 编写目标类
package com.bjpowernode;
public interface ITargetClass {
/**
* 前置增强的方法
*/
void beforeMethod();
/**
* 后置增强的方法
*/
String afterMethod(String name1);
/**
* 环绕增强的方法
*/
void aroundMethod();
/**
* 异常增的方法
*/
void runExceptionMethod();
}
package com.bjpowernode.impl;
import com.bjpowernode.ITargetClass;
public class TargetClassImpl implements ITargetClass {
@Override
public void beforeMethod() {
System.out.println("待前置增强--------beforeMethod");
}
@Override
public String afterMethod(String name) {
System.out.println("待后置增强--------afterMethod");
return "韩梅梅";
}
@Override
public void aroundMethod() {
System.out.println("待环绕增强--------aroundMethod");
}
@Override
public void runExceptionMethod() {
System.out.println("待异常增强--------runExceptionMethod");
int m = 0;
int n = 100/m;
}
}
4.3.3.4 配置AspectJ的增强配置
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 定义增强的目标类对象 -->
<bean id="targetClass" class="com.bjpowernode.impl.TargetClassImpl" />
<!-- 定义增强类的对象 -->
<bean id="myAdvice" class="com.bjpowernode.advice.MyAdvice" />
<!-- 进行AOP配置 -->
<aop:config>
<!-- 前置切点 -->
<!--
aop:before : aspectJ中 前置增强的配置
method : 当前增强类中前置增强的方法 方法名
pointcut-ref : 增强连接点
-->
<aop:pointcut id="beforPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.beforeMethod(..)) " />
<!-- 后置切点 -->
<!-- args 配置被增强的方法的参数名称 -->
<aop:pointcut id="afterPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.afterMethod(..)) and args(name)" />
<!-- 环绕切点 -->
<aop:pointcut id="aroundPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.aroundMethod(..))" />
<!-- 异常切点 -->
<aop:pointcut id="exceptionPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.runExceptionMethod(..))" />
<aop:aspect ref="myAdvice">
<!-- <aop:before method="beforAdvice" pointcut-ref="beforPoint" />-->
<!--
arg-names :后置增强中增强的方法的参数名称
注意: name 也是被增强的方法的参数名称 参数名称要一致
returning :返回结果的参数名称
-->
<aop:after-returning method="afterAdvice" pointcut-ref="afterPoint" arg-names="name,rs" returning="rs" />
<!--<aop:around method="aroundAdvice" pointcut-ref="aroundPoint" />-->
<!--
throwing : 接收异常参数的名称
-->
<aop:after-throwing method="exceptionAdvice" pointcut-ref="exceptionPoint" throwing="exception" />
</aop:aspect>
</aop:config>
</beans>
4.3.3.5 进行测试
package com.bjpowernode;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
ITargetClass bean = context.getBean(ITargetClass.class);
bean.runExceptionMethod();
}
}
4.3.4 AspectJ的AOP注解方式
4.3.4.1 引入相关jar包
<?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>org.example</groupId>
<artifactId>02-spring-aop3</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.2.0.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
4.3.4.2 定义增强类
package com.bjpowernode.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAdvice {
@Before(value = "execution(* com.bjpowernode.impl.TargetClassImpl.beforeMethod(..))")
public void beforAdvice(){
System.out.println("前置增强的方法");
}
/*
*args 被增强的方法的参数名称
* argNames 增强方法的参数名称
* 参数名称必须一致
* returning 参数名称
* */
@AfterReturning(value="execution(* com.bjpowernode.impl.TargetClassImpl.afterMethod(..)) && args(name)",argNames = "name,rs" ,returning = "rs" )
public void afterAdvice(String name,String rs){
System.out.println("后置增强的方法");
}
@Around(value="execution(* com.bjpowernode.impl.TargetClassImpl.aroundMethod(..))")
public void aroundAdvice(ProceedingJoinPoint joinPoint){
System.out.println("前置增强");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("后置增强");
}
/**
* 异常增强
* @param exception
*/
@AfterThrowing(value="execution(* com.bjpowernode.impl.TargetClassImpl.runExceptionMethod(..))",throwing = "exception" )
public void exceptionAdvice(Exception exception){
System.out.println("异常增强!");
}
}
4.3.4.3 目标类
package com.bjpowernode;
public interface ITargetClass {
/**
* 前置增强的方法
*/
void beforeMethod();
/**
* 后置增强的方法
*/
String afterMethod(String name);
/**
* 环绕增强的方法
*/
void aroundMethod();
/**
* 异常增的方法
*/
void runExceptionMethod();
}
package com.bjpowernode.impl;
import com.bjpowernode.ITargetClass;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
public class TargetClassImpl implements ITargetClass {
@Override
public void beforeMethod() {
System.out.println("待前置增强--------beforeMethod");
}
@Override
public String afterMethod(String name) {
System.out.println("待后置增强--------afterMethod");
return "韩梅梅";
}
@Override
public void aroundMethod() {
System.out.println("待环绕增强--------aroundMethod");
}
@Override
public void runExceptionMethod() {
System.out.println("待异常增强--------runExceptionMethod");
int m = 0;
int n = 100/m;
}
}
4.3.4.4 开启相关注解
<?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:context="http://www.springframework.org/schema/context"
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 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:component-scan base-package="com.*" />
<!-- 开启aspectj 的相关注解 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
4.3.4.5 测试类
package com.bjpowernode;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
ITargetClass bean = context.getBean(ITargetClass.class);
bean.runExceptionMethod();
}
}
Spring整合Mybatis就是将Mybatis交给Spring进行管理,将Mybatis的SqlSession对象,放入IOC容器中,并且可以利用自动装配功能,为每个数据库操作层,注入SqlSession。并且Spring内置可以有代理的,可以根据SqlSession对象及数据操作接口,创建Mapper接口的代理对象,此时Mapper的代理在IOC容器中,那么可以将Mapper接口的对象注入到Service中。
使用配置文件版本,Mybatis配置文件和Spring配置文件是单独的。
5.1.1 引入相关jar包
5.1.1.1 spring相关jar包
<!-- spring相关jar包 开始 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!-- spring相关jar包 结束 -->
5.1.1.2 mybatis相关jar包
<!-- mybatis核心包 开始-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- mybatis核心包 结束-->
5.1.1.3 数据库相关jar包
<!-- mysql jdbc 数据库包 开始 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!-- mysql jdbc 数据库包 结束 -->
5.1.1.4 日志相关jar包
<!-- 日志相关jar包 开始 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.3</version>
</dependency>
<!-- 日志相关jar包 结束 -->
5.1.1.5 spring和mybatis整合包
<!-- spring和 mybatis 整合包 开始 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.4</version>
</dependency>
<!-- spring和 mybatis 整合包 结束-->
5.1.1.6 mybatis分页插件包
<!-- mybatis分页插件包 开始-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
<!-- mybatis分页插件包 结束-->
5.1.2 相关类
5.1.2.1 domain
package com.bjpowernode.domain;
public class User {
private Integer id;
private String username;
private String password;
private String realname;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRealname() {
return realname;
}
public void setRealname(String realname) {
this.realname = realname;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", realname='" + realname + '\'' +
'}';
}
}
5.1.2.2 mapper
package com.bjpowernode.mapper;
import com.bjpowernode.domain.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface UserMapper {
/**
* 根据ID 查询用户
* @param id
* @return
*/
User selectById(@Param("id") Integer id);
/**
* 查询所有
* @return
*/
List<User> selectAll();
}
5.1.2.3 service
package com.bjpowernode.service;
import com.github.pagehelper.PageInfo;
import com.bjpowernode.domain.User;
public interface IUserService {
User queryUser(Integer id);
PageInfo<User> queryPage(Integer page,Integer limit);
}
package com.bjpowernode.service.impl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.bjpowernode.domain.User;
import com.bjpowernode.mapper.UserMapper;
import com.bjpowernode.service.IUserService;
public class UserServiceImpl implements IUserService {
private UserMapper userMapper;
@Override
public User queryUser(Integer id) {
return userMapper.selectById(id);
}
@Override
public PageInfo<User> queryPage(Integer page,Integer limit) {
Page<User> users = PageHelper.startPage(page, limit);
userMapper.selectAll();
return users.toPageInfo();
}
/**
* 使用xml形式注入 mapper
* @param userMapper
*/
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
}
5.1.2.4 测试类
package com.bjpowernode;
import com.github.pagehelper.PageInfo;
import com.bjpowernode.domain.User;
import com.bjpowernode.service.IUserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
IUserService bean = context.getBean(IUserService.class);
PageInfo<User> pageInfo = bean.queryPage(2, 1);
List<User> list = pageInfo.getList();
for (User user : list) {
System.out.println(user);
}
}
}
5.1.3 相关配置文件
5.1.3.1 jdbc配置文件
#数据库连接信息
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&useSSL=false&characterEncoding=UTF8&serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
5.1.3.2 日志配置文件
# 全局日志配置
log4j.rootLogger=DEBUG, stdout
# MyBatis 日志配置
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
5.1.3.3 mybatis核心配置文件
<?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="jdbc.properties" />-->
<!-- 全局设置 -->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<!-- 配置类别名 -->
<typeAliases>
<package name="com.bjpowernode.domain"/>
</typeAliases>
<!-- 配置分页插件 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor" />
</plugins>
<!-- 数据源环境 -->
<!-- <environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>-->
<!-- 映射文件 -->
<mappers>
<mapper resource="mapper/UserMapper.xml" />
</mappers>
</configuration>
5.1.3.4 mybatis映射文件
<?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.bjpowernode.mapper.UserMapper">
<select id="selectById" resultType="com.bjpowernode.domain.User">
select * from user where id = #{id}
</select>
<select id="selectAll" resultType="com.bjpowernode.domain.User">
select * from user
</select>
</mapper>
5.1.3.5 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: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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--
终极目标 为 每个service 注入一个Mapper 对象
1. Mapper 对象
2. SqlSession 获取
3. SqlSession 要根据 SqlSessionFactory 获取
4. SqlSessionFactory 要 根据 SqlSessionFactoryBuilder获取
5. SqlSessionFactoryBuilder 可以直接创建 此时需要使用IOC容器
6. 配置数据源
-->
<!--1. 引入jdbc的配置文件 使用配置文件 不是使用系统属性-->
<context:property-placeholder location="jdbc.properties" system-properties-mode="FALLBACK" />
<!--2. 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--3. 配置SqlSessionFactoryBean 对象-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- mybatis 核心配置文件 -->
<property name="configLocation" value="mybatis.xml" />
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 4.扫描所有的Mapper接口 自动创建Mapper的代理对象 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 待扫描的mapper 接口的包名 多个之间使用逗号分隔 -->
<property name="basePackage" value="com.bjpowernode.mapper" />
<!--注入SqlSessionFactoryBean 用于创建SqlSession -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean" />
</bean>
<!-- 配置Service 层对象 -->
<bean id="userService" class="com.bjpowernode.service.impl.UserServiceImpl" autowire="byType" />
</beans>
使用Spring的配置文件,取代mybatis的核心配置文件。
导入的jar包和相关类完全一致,jdbc配置文件和日志配置文件也相同。只是将mybatis的核心配置文件中的配置,移入到spring的核心配置文件中。
5.2.1 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: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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--
终极目标 为 每个service 注入一个Mapper 对象
1. Mapper 对象
2. SqlSession 获取
3. SqlSession 要根据 SqlSessionFactory 获取
4. SqlSessionFactory 要 根据 SqlSessionFactoryBuilder获取
5. SqlSessionFactoryBuilder 可以直接创建 此时需要使用IOC容器
6. 配置数据源
-->
<!--1. 引入jdbc的配置文件 使用配置文件 不是使用系统属性-->
<context:property-placeholder location="jdbc.properties" system-properties-mode="FALLBACK" />
<!--2. 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--3. 配置SqlSessionFactoryBean 对象-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 配置数据源 -->
<property name="dataSource" ref="dataSource" />
<!-- 配置包下类别名 多个包之间使用 逗号分隔 -->
<property name="typeAliasesPackage" value="com.bjpowernode.domain" />
<!-- 配置插件 -->
<property name="plugins">
<array>
<bean id="pageInterceptor" class="com.github.pagehelper.PageInterceptor" />
</array>
</property>
<!-- 配置映射文件 -->
<property name="mapperLocations" value="mapper/**/*Mapper.xml"></property>
</bean>
<!-- 4.扫描所有的Mapper接口 自动创建Mapper的代理对象 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 待扫描的mapper 接口的包名 多个之间使用逗号分隔 -->
<property name="basePackage" value="com.bjpowernode.mapper" />
<!--注入SqlSessionFactoryBean 用于创建SqlSession -->
<!--
SqlSessionFactoryBean 是可以自动装配的 但是 如果存在多个数据源时,可以指定SqlSessionFactoryBean
区分数据源
-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean" />
</bean>
<!-- 配置Service 层对象 -->
<bean id="userService" class="com.bjpowernode.service.impl.UserServiceImpl" autowire="byType" />
</beans>
5.2.2 配置日志
<?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: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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--
终极目标 为 每个service 注入一个Mapper 对象
1. Mapper 对象
2. SqlSession 获取
3. SqlSession 要根据 SqlSessionFactory 获取
4. SqlSessionFactory 要 根据 SqlSessionFactoryBuilder获取
5. SqlSessionFactoryBuilder 可以直接创建 此时需要使用IOC容器
6. 配置数据源
-->
<!--1. 引入jdbc的配置文件 使用配置文件 不是使用系统属性-->
<context:property-placeholder location="jdbc.properties" system-properties-mode="FALLBACK" />
<!--2. 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置 Configuration-->
<bean id="configuration" class="org.apache.ibatis.session.Configuration">
<!-- 指定日志工具 -->
<property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl" />
<!-- 配置缓存 -->
<property name="cacheEnabled" value="true" />
</bean>
<!--3. 配置SqlSessionFactoryBean 对象-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 配置数据源 -->
<property name="dataSource" ref="dataSource" />
<!-- 配置包下类别名 多个包之间使用 逗号分隔 -->
<property name="typeAliasesPackage" value="com.bjpowernode.domain" />
<!-- 配置插件 -->
<property name="plugins">
<array>
<bean id="pageInterceptor" class="com.github.pagehelper.PageInterceptor" />
</array>
</property>
<!-- 注入configuration -->
<property name="configuration" ref="configuration" />
<!-- 配置映射文件 -->
<property name="mapperLocations" value="mapper/**/*Mapper.xml"></property>
</bean>
<!-- 4.扫描所有的Mapper接口 自动创建Mapper的代理对象 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 待扫描的mapper 接口的包名 多个之间使用逗号分隔 -->
<property name="basePackage" value="com.bjpowernode.mapper" />
<!--注入SqlSessionFactoryBean 用于创建SqlSession -->
<!--
SqlSessionFactoryBean 是可以自动装配的 但是 如果存在多个数据源时,可以指定SqlSessionFactoryBean
区分数据源
-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean" />
</bean>
<!-- 配置Service 层对象 -->
<bean id="userService" class="com.bjpowernode.service.impl.UserServiceImpl" autowire="byType" />
</beans>
在spring中,spring可以管理数据源,管理连接,spring也可以管理事务,并且spring单独对事务分了一个模块进行管理。并且,Spring简化了事务开发,只需要通过配置的方式,Spring即可对事务进行统一完善的管理,Spring的事务管理基于Spring的AOP技术。
Spring的声明式事务,有两种方式: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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--
终极目标 为 每个service 注入一个Mapper 对象
1. Mapper 对象
2. SqlSession 获取
3. SqlSession 要根据 SqlSessionFactory 获取
4. SqlSessionFactory 要 根据 SqlSessionFactoryBuilder获取
5. SqlSessionFactoryBuilder 可以直接创建 此时需要使用IOC容器
6. 配置数据源
-->
<!--1. 引入jdbc的配置文件 使用配置文件 不是使用系统属性-->
<context:property-placeholder location="jdbc.properties" system-properties-mode="FALLBACK" />
<!--2. 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置 Configuration-->
<bean id="configuration" class="org.apache.ibatis.session.Configuration">
<!-- 指定日志工具 -->
<property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl" />
<!-- 配置缓存 -->
<property name="cacheEnabled" value="true" />
</bean>
<!--3. 配置SqlSessionFactoryBean 对象-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 配置数据源 -->
<property name="dataSource" ref="dataSource" />
<!-- 配置包下类别名 多个包之间使用 逗号分隔 -->
<property name="typeAliasesPackage" value="com.bjpowernode.domain" />
<!-- 配置插件 -->
<property name="plugins">
<array>
<bean id="pageInterceptor" class="com.github.pagehelper.PageInterceptor" />
</array>
</property>
<!-- 注入configuration -->
<property name="configuration" ref="configuration" />
<!-- 配置映射文件 -->
<property name="mapperLocations" value="mapper/**/*Mapper.xml"></property>
</bean>
<!-- 4.扫描所有的Mapper接口 自动创建Mapper的代理对象 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 待扫描的mapper 接口的包名 多个之间使用逗号分隔 -->
<property name="basePackage" value="com.bjpowernode.mapper" />
<!--注入SqlSessionFactoryBean 用于创建SqlSession -->
<!--
SqlSessionFactoryBean 是可以自动装配的 但是 如果存在多个数据源时,可以指定SqlSessionFactoryBean
区分数据源
-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean" />
</bean>
<!-- 配置Service 层对象 -->
<bean id="userService" class="com.bjpowernode.service.impl.UserServiceImpl" autowire="byType" />
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 配置数据源 指定要管理那个数据源的事务 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置事务增强 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 配置增强规则 -->
<tx:attributes>
<!--
name : 进行数据库操作方法的名称 add* 表示 add开头的方法 * 指代任意字符
propagation : 事务传播性 面试重点
read-only : 只读事务 默认 false
rollback-for : 指定回滚的异常 默认是 RunTimeException 下的异常会自动回滚
no-rollback-for : 不回滚的异常
timeout : 事务的超时时间
isolation : 事务隔离级别 面试重点
| 1. 读未提交
| 2. 读已提交
| 3. 可重复读
| 4. 串行化
-->
<tx:method name="add*" read-only=”false” rollback-for="Exception"/>
<tx:method name="insert*" rollback-for="Exception" />
<tx:method name="update*" rollback-for="Exception" />
<tx:method name="delete*" rollback-for="Exception" />
<tx:method name="*" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- 配置切面 -->
<aop:config>
<!-- 注意一般切点在 service -->
<aop:pointcut id="mypoint" expression="execution(* com.bjpowernode.service.impl.*.*(..))"/>
<!-- 织入增强 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="mypoint" />
</aop:config>
</beans>
在Spring中,为简化事务开发,提供了注解:@Transactional,该注解可以指定在类上,在类上,该类中的所有方法都会使用事务,该注解也可以指定方法上,该方法会使用事务。使用非常简单,只需:
配置事务管理器
开启注解事务
在需要使用事务的地方使用@Transactional
6.2.1 配置类
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--
终极目标 为 每个service 注入一个Mapper 对象
1. Mapper 对象
2. SqlSession 获取
3. SqlSession 要根据 SqlSessionFactory 获取
4. SqlSessionFactory 要 根据 SqlSessionFactoryBuilder获取
5. SqlSessionFactoryBuilder 可以直接创建 此时需要使用IOC容器
6. 配置数据源
-->
<!--1. 引入jdbc的配置文件 使用配置文件 不是使用系统属性-->
<context:property-placeholder location="jdbc.properties" system-properties-mode="FALLBACK" />
<!--2. 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置 Configuration-->
<bean id="configuration" class="org.apache.ibatis.session.Configuration">
<!-- 指定日志工具 -->
<property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl" />
<!-- 配置缓存 -->
<property name="cacheEnabled" value="true" />
</bean>
<!--3. 配置SqlSessionFactoryBean 对象-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 配置数据源 -->
<property name="dataSource" ref="dataSource" />
<!-- 配置包下类别名 多个包之间使用 逗号分隔 -->
<property name="typeAliasesPackage" value="com.bjpowernode.domain" />
<!-- 配置插件 -->
<property name="plugins">
<array>
<bean id="pageInterceptor" class="com.github.pagehelper.PageInterceptor" />
</array>
</property>
<!-- 注入configuration -->
<property name="configuration" ref="configuration" />
<!-- 配置映射文件 -->
<property name="mapperLocations" value="mapper/**/*Mapper.xml"></property>
</bean>
<!-- 4.扫描所有的Mapper接口 自动创建Mapper的代理对象 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 待扫描的mapper 接口的包名 多个之间使用逗号分隔 -->
<property name="basePackage" value="com.bjpowernode.mapper" />
<!--注入SqlSessionFactoryBean 用于创建SqlSession -->
<!--
SqlSessionFactoryBean 是可以自动装配的 但是 如果存在多个数据源时,可以指定SqlSessionFactoryBean
区分数据源
-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean" />
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 配置数据源 指定要管理那个数据源的事务 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 开启组件扫描 -->
<context:component-scan base-package="com.*" />
<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
6.2.2 service代码
package com.bjpowernode.service.impl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.bjpowernode.domain.User;
import com.bjpowernode.mapper.UserMapper;
import com.bjpowernode.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public User queryUser(Integer id) {
return userMapper.selectById(id);
}
@Override
public PageInfo<User> queryPage(Integer page,Integer limit) {
Page<User> users = PageHelper.startPage(page, limit);
userMapper.selectAll();
return users.toPageInfo();
}
/**
* 在spring中 不要在事务相关方法中 使用catch 处理
* 如果处理了 异常不会被spring aop 增强的方法 捕获到 不会回滚
* 事务配置就失效了 所以如果有异常 :
* 1. 直接抛
* 2. 在catch继续抛出异常
* @param user
* @return
*/
@Override
//@Transactional(rollbackFor = Exception.class)
public Integer addUser(User user) {
int m = userMapper.insert(user);
int n = 0;
try {
m = m/n;//出现异常
} catch (Exception exception) {
exception.printStackTrace();
throw new RuntimeException("发生 请求回滚!!!");
}
return m;
}
}
注意:
事务的传播行为,是指事务会发生传递。例如:A方法存在事务,A调用B方法,那么B方法也会在事务中执行,这种就是事务的传播行为。
package com.bjpowernode.controller;
import com.bjpowernode.domain.User;
import com.bjpowernode.mapper.UserMapper;
import com.bjpowernode.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
@Controller
public class UserController {
@Autowired
private IUserService userService;
@Autowired
private UserMapper userMapper;
@Transactional
public void add(User user){
User user1 = new User();
user1.setUsername("韩梅梅");
user1.setPassword("123");
user1.setRealname("韩梅梅");
userMapper.insert(user1);//在父事务中添加数据 自己的程序 更新数据状态
try {
//其他的业务操作
userService.addUser(user);//子事务 子事务发生异常
}catch (Exception e){
e.printStackTrace();
}
int n = 0;
//n = 100/n;//出现异常
}
}
package com.bjpowernode.service.impl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.bjpowernode.domain.User;
import com.bjpowernode.mapper.UserMapper;
import com.bjpowernode.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public User queryUser(Integer id) {
return userMapper.selectById(id);
}
@Override
public PageInfo<User> queryPage(Integer page,Integer limit) {
Page<User> users = PageHelper.startPage(page, limit);
userMapper.selectAll();
return users.toPageInfo();
}
/**
* 在spring中 不要在事务相关方法中 使用catch 处理
* 如果处理了 异常不会被spring aop 增强的方法 捕获到 不会回滚
* 事务配置就失效了 所以如果有异常 :
* 1. 直接抛
* 2. 在catch继续抛出异常
* @param user
* @return
*/
@Override
//@Transactional(propagation = Propagation.SUPPORTS) //有就在事务中执行 没有就不在事务中执行
//@Transactional(propagation = Propagation.MANDATORY) //必须要有事务
//@Transactional(propagation = Propagation.NEVER) // 不在事务中执行 当前存在事务就报错
@Transactional(propagation = Propagation.NESTED) // 当前存在一个事务 就 创建一个子事务 嵌套在当前事务中
//外层事务出现异常会回滚子事务 子事务出现异常 不会回滚外层事务
public Integer addUser(User user) {
int m = userMapper.insert(user);
int n = 0;
m = m/n;//出现异常
return m;
}
}