1、Spring
1.1、简介
-
2004,以interface21为基础,发表了Spring1.0。
-
Rod Johnson,Spring Framework创始人,著名作者。 Rod在悉尼大学不仅获得了计算机学位,同时还获得了音乐学位。
-
spring理念:使现有技术的使用更加容易。
-
SSH:Structs+Spring+Hibernate
-
SSM:SpringMVC+Spring+Mybatis
官网:https://spring.io/projects/spring-framework#overview
官方下载地址:https://repo.spring.io/release/org/springframework/spring/
github:https://github.com/spring-projects/spring-framework
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
1.2、优点
- spring是开源,免费的容器。
- spring是一个轻量级,非侵入性的框架。
- 控制反转(IOC),面向切面编程(AOP)。
- 支持事务处理,支持框架整合。
Spring是一个轻量级的IOC和AOP的框架!
1.3、组成
1.4、拓展
- Spring Boot:
- 快速开发的脚手架。
- 基于SpringBoot可以快速开发单个微服务。
- 约定大于配置
- Spring Cloud:
- 基于SpringBoot实现。
目前,公司大多采用springboot可开发。掌握SpringBoot的前提是掌握Spring和SpringMVC。
Spring弊端:发展太久后,配置十分繁琐,没有简化开发的作用了。
2、IOC理论推导
1.UserDao接口
2.UserDaoImpl实现类
3.UserService接口
4.UserServiceImpl实现类
之前业务代码,会根据用户需求而更改UserDaoImpl,修改原代码的成本很高。
使用一个Set接口实现,发生改变!
//之前
private UserDao userDao=new UserDaoImpl();
//之后
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
- 之前,程序主动创建对象,控制权在程序员处。
- 之后,程度被动接受对象,控制权在用户处。
这种思想,由程序员控制来转化为用户控制,程序员不管理对象的创建,由用户来管理,这是IOC的原型。
IOC本质
控制反转,是一种设计思想,DI(依赖注入)是IOC的一种实现方式。控制反转的本质是获得依赖对象的方式反转了。(对象的创建由程序自己控制,转移给第三方。)
控制反转是通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。Spring中实现控制反转的是IOC容器,实现方式是依赖注入(DI)。
- 采用XML方式:Bean的定义信息是和实现分离的。
- 采用注解方式:Bean的定义信息直接以注解的形式定义在实体类中,达到零配置的目的。
3、HelloSpring
1、建实体类
public class Hello
{
private String str;
public String getStr(){
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "hello{" +
"str='" + str + '\'' +
'}';
}
}
2、配置beans.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hello" class="com.plancer.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
</beans>
3、测试程序
public class MyTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
- 理解IOC
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="UserDaoMysqlImpl" class="com.plancer.dao.UserDaoMysqlImpl">
</bean>
<bean id="UserServiceImpl" class="com.plancer.service.UserServiceImpl">
<property name="userDao" ref="UserDaoMysqlImpl"/>
</bean>
</beans>
用户不需要修改测试程序,程序员不需要修改UserService接口的实现类,只要更改xml配置文件即可。如:修改property的ref为不同id的bean。
4、IOC创建对象的方式
1、默认使用无参构造创建对象
2、如果要使用有参构造创建对象
1、下标赋值
<bean id="User" class="com.plancer.pojo.User">
<constructor-arg index="0" value="plancer"/>
</bean>
2、类型赋值
<bean id="User" class="com.plancer.pojo.User">
<constructor-arg type="java.lang.String" value="plancer"/>
</bean>
3、名称赋值(推荐)
<bean id="User" class="com.plancer.pojo.User">
<constructor-arg name="name" value="plancer"/>
</bean>
总结:在配置文件加载时,容器中管理的所有对象就已经初始化了。
容器:ApplicationContext context = new ClassPathXmlApplicationContext(“beans.xml”);
5、Spring配置
5.1、别名
<!--添加了别名,可通过别名来获取这个对象-->
<alias name="User" alias="user"/>
5.2、bean的配置
<!--
id:bean的唯一标识符,相当于对象名
class:bean对象所对应的全限定名:包名+类名
name:也是别名,且可以绑定多个别名
-->
<bean id="User" class="com.plancer.pojo.User" name="user2,user3;user4 user5">
<constructor-arg name="name" value="plancer"/>
</bean>
5.3、import
import一般在团队开发时使用,将多个配置文件,导入合并为一个
如:三个程序员负责不同的类的开发,不同的类需要注册在不同的bean中,可以利用import将所有的人beans.xml合并为一个总的applicationContext.xml。
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
6、依赖注入
6.1、构造器注入
6.2、Set方式注入
-
依赖注入:Set注入
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象的所有属性,由容器来注入
1.复杂类型
public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
2.实体类
public class Student { private String name; private Address address; private String[] books; private List<String> hobby; private Map<String,String> card; private Set<String> games; private String wife; private Properties info; }
3.beans.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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="student" class="com.plancer.pojo.Student"> <!--普通值注入:value--> <property name="name" value="plancer"/> </bean> </beans>
4.测试类
public class Mytest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.getName()); } }
完善注入信息:
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="address" class="com.plancer.pojo.Address"> <property name="address" value="杭州"/> </bean> <bean id="student" class="com.plancer.pojo.Student"> <!--普通值注入:value--> <property name="name" value="plancer"/> <!--注入其他bean的id(ref),属性为其他类--> <property name="address" ref="address"/> <!--数组--> <property name="books"> <array> <value>水浒传</value> <value>西游记</value> </array> </property> <!--List--> <property name="hobby"> <list> <value>Music</value> <value>Film</value> </list> </property> <!--Map--> <property name="card"> <map> <entry key="name" value="plancer"/> <entry key="pwd" value="123123"/> </map> </property> <!--set--> <property name="games"> <set> <value>DMC</value> </set> </property> <property name="wife"> <null/> </property> <!--Properties--> <property name="info"> <props> <prop key="name">Virgle</prop> <prop key="age">23</prop> </props> </property> </bean> </beans>
测试类:
public class Mytest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.toString()); // Student{name='plancer', // address=Address{address='杭州'}, // books=[水浒传, 西游记], // hobby=[Music, Film], // card={name=plancer, pwd=123123}, // games=[DMC], // wife='null', // info={age=23, name=Virgle}} } }
6.3、拓展方式注入:
- p命名空间:
p相当于properties
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="classic" class="com.example.ExampleBean">
<property name="email" value="someone@somewhere.com"/>
</bean>
<bean name="p-namespace" class="com.example.ExampleBean"
p:email="someone@somewhere.com"/>
</beans>
- c命名空间:
c表示有参构造器:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
<!-- traditional declaration with optional argument names -->
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg name="thingTwo" ref="beanTwo"/>
<constructor-arg name="thingThree" ref="beanThree"/>
<constructor-arg name="email" value="something@somewhere.com"/>
</bean>
<!-- c-namespace declaration with argument names
如果是基本类型:c:name=
如果是引用类型:c:name-ref=
-->
<bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>
</beans>
注意:两者都需要在头部引入相应的xml约束
P:xmlns:p="http://www.springframework.org/schema/p"
C:xmlns:c="http://www.springframework.org/schema/c"
6.4、bean的作用域
1.单例模式(Spring默认机制)
从容器中get的对象是一个对象
<bean id="address" class="com.plancer.pojo.Address" scope="singleton">
<property name="address" value="杭州"/>
</bean>
2.原型模式
每次从容器中get的对象都是一个新的对象。
<bean id="address" class="com.plancer.pojo.Address" scope="prototype">
<property name="address" value="杭州"/>
</bean>
3.request,session,application等只能在web开发中使用到。
7、Bean的自动装配
- 自动装配是Spring满足bean依赖的一种方式
- Spring从上下文中自动寻找,自动给bean装配属性
Spring中的三种装配方式:
- 在xml中显式的配置
- 在java中显式的配置
- 隐式的自动配置bean【重要】
7.1、测试
1.环境搭建
- 人有两个宠物
7.2、ByName自动注入
自动在容器上下文中查找,和自己对象set方法的值对应的bean的id
<bean id="dog" class="com.plancer.pojo.Dog"/>
<bean id="cat" class="com.plancer.pojo.Cat"/>
<bean id="people" class="com.plancer.pojo.People" autowire="byName">
</bean>
7.3、ByType自动注入
自动在容器上下文中查找,和对象set方法指向的属性类型相同的bean
<bean class="com.plancer.pojo.Dog"/>
<bean class="com.plancer.pojo.Cat"/>
<bean id="people" class="com.plancer.pojo.People" autowire="byType">
</bean>
小结:
- byName:需要保证bean的id一致
- byName:需要保证bean的class一致
注意点:
1、导入约束:context约束
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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="com.plancer.pojo.Dog"/>
<bean class="com.plancer.pojo.Cat"/>
<bean id="people" class="com.plancer.pojo.People" autowire="byType">
</bean>
</beans>
@Autowired
直接在属性上使用注解,或者在set方法上使用。
使用Autowired可以不写set方法,前提是自动装配的属性在IOC容器中存在,且符合类型ByType。
其他点:
@Nullable 字段标记了这个注释,说明这个字段可以为null
public @interface Autowired {
boolean required() default true;
}
测试代码
public class People {
//若定义了false,则该属性可以为空
@Autowired(required = false)
private Dog dog;
@Autowired
private Cat cat;
}
如果@Autowired无法按照类型完成自动装配时,可以通过@Qualifier(value=“xxx”)来配合Autowired一起使用,指定一个唯一bean的id。
public class People {
@Autowired
@Qualifier(value = "dog22")
private Dog dog;
@Autowired
private Cat cat;
}
@Resource注解
@Resource
private Cat cat;
@Autowired和@Resource的区别:
- 同:都用来自动装配,都可以放在属性字段上
- 异:
- @Autowired默认使用bytpe方式实现,再通过byname实现,且要求该对象存在;
- @Resource默认通过byname方式实现,找不到名字,再通过bytype实现
8、使用注解开发
1.开启注解支持,且扫描某个包下的所有component
<?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
https://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.plancer.pojo"/>
<context:annotation-config/>
</beans>
2.属性如何注入
@Component
public class User {
public String name;
//相当于<property name="name" value="plancer"/>
@Value("plancer")
public void setName(String name){
this.name=name;
}
}
3.衍生的注解
@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层。
- dao【@Repository】
- service【@Service】
- controller【@Controller】
四个注解的功能一致,都是表示某个类被注册到Spring容器中,装配bean。
4、自动装配
- @Autowired:自动装配属性,通过类型,名字
- @Nullable: 说明该字段可以为空
- @Resource:自动装配属性,通过名字,类型
5、作用域
@Component
@Scope("prototype")
public class User {
public String name;
@Value("plancer")
public void setName(String name){
this.name=name;
}
}
6、小结
xml与注解:
- xml更万能,适用于任何场合,维护简单
- 注解更简单,但是不是自己类不能使用,维护复杂
xml和注解的最佳实践:
- xml用来管理bean
- 注解只用来完成属性的注入
9、使用Java的方式配置Spring
不使用Spring的xml配置,而使用JavaConfig来做。
1、实体类
public class User {
private String name;
public String getName() {
return name;
}
@Value("long")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
2、配置类
@Configuration
public class plancerConfig {
//方法名相当于bean的id,返回类型相当于bean的class
@Bean
public User getUser(){
return new User();
}
}
3、测试类
public class MyTest {
public static void main(String[] args) {
//用配置类方式自动配置,就需要使用AnnotationConfig上下文来获取容器,参数是配置类.class
ApplicationContext context = new AnnotationConfigApplicationContext(plancerConfig.class);
User user = (User) context.getBean("getUser");
System.out.println(user.getName());
}
}
以上方式常见于springboot。
springboot启动类的注解@SpringBootApplication的底层@SpringBootConfiguration,其底层是@Configuration,其底层是@Component
10、代理模式
10.1 静态代理
角色:
- 抽象角色:一般使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,会做一些附属操作
- 客户:访问代理对象的人
代码步骤:
1.接口
public interface Rent {
void rent();
}
2.真实角色
//房东
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
3.代理角色
//组合方式(被代理类是代理类的属性)
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
seeHouse();
host.rent();
hetong();
fee();
}
//其他附属操作
public void seeHouse(){
System.out.println("中介带看房");
}
public void hetong(){
System.out.println("签合同");
}
public void fee(){
System.out.println("收中介费");
}
}
4.客户端访问代理角色
public class Client {
public static void main(String[] args) {
Host host = new Host();
//代理帮房东租房,代理会有一些附属操作
Proxy proxy = new Proxy(host);
//客户面向中介租房
proxy.rent();
}
}
代理模式的优点:
- 真实角色的业务更加纯粹,不用考虑公共业务
- 公共业务由代理角色实现,公共业务和主要业务分工
- 公共业务发生扩展,只需要在代理类中添加,方便管理
缺点 :
- 一个真实角色就会产生一个代理角色,代码量翻倍
10.2 静态代理再理解
1.抽象接口
//抽象接口
public interface UserService {
void add();
void delete();
void update();
void query();
}
2.真实角色
//真实对象,核心业务不改变
public class UserServiceImp1 implements UserService{
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("修改了一个用户");
}
@Override
public void query() {
System.out.println("查询了一个用户");
}
}
3.代理角色
真实角色是其属性,添加设置真实角色的set方法,代理角色的业务方法通过真实角色属性实现,还可以在代理角色添加一些额外的方法。
//代理对象,可以添加新的功能
public class UserServiceProxy implements UserService {
private UserServiceImp1 userService;
public void setUserService(UserServiceImp1 userService) {
this.userService = userService;
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void delete() {
log("delete");
userService.delete();
}
@Override
public void update() {
log("update");
userService.update();
}
@Override
public void query() {
log("query");
userService.query();
}
//日志方法
public void log(String msg){
System.out.println("使用了" + msg + "方法");
}
}
4.客户端调用代理角色
public class Client {
public static void main(String[] args) {
UserServiceImp1 userService = new UserServiceImp1();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.add();
proxy.delete();
proxy.update();
proxy.query();
}
}
10.3 动态代理
- 动态代理角色与静态代理角色相同
- 动态代理类是动态生成
- 动态代理分类:
- 基于接口:JDK动态代理(这里使用)
- 基于类:cglib
- java字节码:javasist
动态代理的优点:
- 静态代理的所有优点
- 动态代理的是一个接口,对应的一类业务
- 一个动态代理类可以代理多个类,只要这些类实现了同一个接口(target所属于的接口),而不需要改变动态代理类的代码(ProxyInvovationHandler)
1.InvocationHandler的实现类
this表示当前方法所在的类
- 解读:
1.Proxy.newProxyInstance(参数1,参数2,参数3)
参数1:表示用哪个类来加载生成的代理类
参数2:表示要代理的接口(生成的代理类的接口)
参数3:实现了InvocationHandler的类,需要实现一个方法invoke(Object proxy, Method method, Object[] args)
2.invoke(Object proxy, Method method, Object[] args)
参数1:生成的代理类
参数2:要代理某个对象,所要增强该对象的某个方法
参数3:执行参数2方法所需的参数
方法内的方法:Object object= method.invoke(obj,args);
obj是要增强的对象,args是执行method需要的参数,object是要返回的值。
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口,如target为Rent的对象
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
//处理代理实例的方法,返回并返回结果,若代理实例不执行方法,此方法不执行
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
//额外方法
public void log(String msg){
System.out.println("执行了" + msg + "方法");
}
}
- 客户端生成代理类,并执行方法
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImp1 userService = new UserServiceImp1();
//代理角色,不存在
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userService);//设置所代理的对象,userService为Target的实现类
UserService proxy = (UserService) pih.getProxy();
proxy.add();
}
}
若需要动态代理该类的另外的实现类,无论该实现类怎样重写了UseService接口的方法,只需要改变动态代理设置其Target属性的对象即可。
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImp2 userService = new UserServiceImp2();
//代理角色,不存在
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userService);//设置所代理的对象,userService为Target的实现类
UserService proxy = (UserService) pih.getProxy();
proxy.add();
}
}
这里,如要动态代理不同接口的实现类,只需要改变实现了不同接口的真实角色即可。动态代理类不需修改如:
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色,不存在
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(host);
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
11、AOP
11.1 简介
AOP(面向切面编程):通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
11.2 AOP在Spring中的作用
提供声明式事务:允许用户自定义切面
-
横切关注点:跨越应用程序多个模块的方法和功能。如:日志,安全,缓存,事务等等。
-
切面:横切关注点被模块化的特殊对象,它是一个类。
-
通知:切面要完成的工作。它是类中的一个方法。
-
目标:被通知对象。
-
代理:向目标对象应用通知之后创建的对象。
-
切入点:切面通知 执行的“地点”的定义。
-
连接点:与切入点匹配的执行点。
SpringAOP中,通过Advice定义横切逻辑,Spring支持5中类型的Advice:
通知类型 | 连接点 | 实现接口 |
---|---|---|
前置通知 | 方法前 | org.springframework.aop.MethodBeforeAdvice |
后置通知 | 方法后 | org.springframework.aop.AfterReturningAdvice |
环绕通知 | 方法前后 | org.aopalliance.intercept.MethodInterceptor |
异常抛出通知 | 方法抛出异常 | org.springframework.aop.ThrowsAdvice |
引介通知 | 类中增加新的方法属性 | org.springframework.aop.IntroductionInterceptor |
AOP在不改变原有代码的情况下,去增加新的功能。
使用AOP需要导入依赖:
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
AOP的实现方式:
- 方式一:使用原生Spring API接口
1.要切入的功能
public class Log implements MethodBeforeAdvice {
//method:要执行的目标对象的方法
//args:参数
//target:目标对象
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + "的" + method.getName() + "被执行了");
}
}
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" + method.getName() + "方法,返回结果为: " + returnValue);
}
}
2.被切入的代码
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="log" class="com.plancer.log.Log"/>
<bean id="afterLog" class="com.plancer.log.AfterLog"/>
<bean id="userService" class="com.plancer.service.UserServiceImpl"/>
<!--方式一:使用原生Spring API接口-->
<aop:config>
<!-- 切入点:expression="execution(修饰符(默认为public,可以不写) 返回值类型 类名.方法名.(参数)"
参数:..表示任意参数;*,String表示第一个参数为任意类型,第二个为String类型
-->
<aop:pointcut id="pointCut" expression="execution(* com.plancer.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointCut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointCut"/>
</aop:config>
</beans>
4.测试类
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是实现了接口的实现类的bean的ID
UserService userService = context.getBean("userService", UserService.class);
userService.select();
}
}
- 方式二:使用自定义切面的方式
<bean id="diy" class="com.plancer.diy.DiyPointCut"/>
<aop:config>
<!--切面(要切入的类)-->
<aop:aspect ref="diy">
<!--切入点(要被切入的位置)-->
<aop:pointcut id="point" expression="execution(* com.plancer.service.UserServiceImpl.*())"/>
<!--通知(切入的类的方法)-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>
- 方式三:使用注解
注解配置切面
@Aspect
public class AnnotationPointCut {
@Before("execution(* com.plancer.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("==方法执行前==");
}
@After("execution(* com.plancer.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("==方法执行后==");
}
}
配置文件注册bean
<bean id="annotationPointCut" class="com.plancer.diy.AnnotationPointCut"/>
<!--开启注解支持,JDK(默认 proxy-target-class="false") cglib(proxy-target-class="true"-->
<aop:aspectj-autoproxy/>
12、整合mybatis
目的:让java测试代码不出现mybatis代码
12.1 Mybatis小结
步骤:
1.导入相关jar包
- junit
- mybatis
- mysql
- spring相关
- aop织入
- mybatis-spring【new】
2.编写配置文件
3.测试
12.2 Mybatis-spring
- 方式一
1.编写数据源配置
<!--使用spring的数据源替换Mybatis的配置-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
2.sqlSessionFactory
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--配置数据源-->
<property name="dataSource" ref="dataSource"/>
<!--绑定Mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/plancer/mapper/*.xml"/>
</bean>
3.sqlSessionTemplate
<!--SqlSessionTemplate:即相当于mybatis使用的sqlsession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--构造器方式注入sqlSessionTemplate所需的sqlSessionFactory-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
4.给接口新增实现类【新增】
让该实现类做原先业务层(测试类)做的事情
public class UserMapperImpl implements UserMapper {
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
5.将实现类注入spring配置文件中,并配置其sqlSession属性
<bean id="userMapper" class="com.plancer.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
6.测试类
public class MyTest {
@Test
public void test() throws IOException {
//通过spring配置文件获取上下文
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
//spring配置文件中注册的bean的id,其类属于的接口.class
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
List<User> users = userMapper.selectUser();
for (User user : users) {
System.out.println(user);
}
}
}
- 方式二
与方式一不同之处:接口的实现类继承SqlSessionDaoSupport,而不需要注入sqlSessionTemplate的属性,同时spring文件中,也不需要注入sqlSessionTemplate的bean
与方式一相同的代码不做重复
不同之处:
1.实现类
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
2.将实现类注入spring配置文件中,并配置sqlSessionFactory
<bean id="userMapper2" class="com.plancer.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
总结:其实两个方式本质一样,都需要数据源,sqlsessionFactory,SqlSessionDaoSupport的getSqlSession方法还是返回的是SqlSessionTemplate(相当于sqlSession),而SqlSessionTemplate是根据sqlSessionFactory创建的。
13、声明式事务
13.1、spring中的事务
- 声明式事务:AOP
- 编程式事务:在代码中进行事务的管理
13.2、为什么需要事务?
- 防止数据提交不一致(前者减去余额,后者没有加上余额)
- 不在spring中配置声明式事务,就需要在代码中手动配置事务(麻烦)
13.3、添加事务实验
制造需要事务的场景,将mapper.xml中的delete语句写错,这样如果没有事务,就会单独执行插入user1,而不能删除id为6的用户,这时就需要事务
1.实现类
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
User user1 = new User(6, "virgil", "111111");
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(user1);
mapper.deleteUser(6);
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
2.在spring配置文件中添加如下代码:
注意:导入tx包
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<import resource="spring-dao.xml"/>
<bean id="userMapper2" class="com.plancer.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--通过AOP实现事务织入-->
<!--配置事务-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--name为需要配置事务的方法名(是实现类的具体sql方法),*表示对所有方法织入事务-->
<tx:method name="selectUser" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.plancer.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
</beans>
其中,spring-dao.xml如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--使用spring的数据源替换Mybatis的配置-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--配置数据源-->
<property name="dataSource" ref="dataSource"/>
<!--绑定Mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/plancer/mapper/*.xml"/>
</bean>
</beans>
mybatis-config如下:
<?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核心配置文件-->
<configuration>
<typeAliases>
<package name="com.plancer.pojo"/>
</typeAliases>
</configuration>
3.测试类
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
}
rk.jdbc.datasource.DriverManagerDataSource">
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--配置数据源-->
<property name="dataSource" ref="dataSource"/>
<!--绑定Mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/plancer/mapper/*.xml"/>
</bean>
~~~
mybatis-config如下:
<?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核心配置文件-->
<configuration>
<typeAliases>
<package name="com.plancer.pojo"/>
</typeAliases>
</configuration>
3.测试类
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
}