1.Spring
1.1 简介
- spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架!
- 官方下载地址:repo.spring.io/release/org/springframework/spring/
- GitHub:github.com/spring-projects/spring-framework
- 文档:docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/core.html#beans-factory-collaborators
1.2 优点
-
spring是一个开源的免费的框架(容器)
-
spring是一个轻量级(本身很小)、非入侵式(引入spring不会改变代码任何情况)的框架
-
控制反转(IOC),面向切面编程(AOP)
-
支持事务的处理,对框架整合的支持
总结:Spring就是一个轻量级的控制反转(IOC),面向切面编程(AOP)的框架。
1.3 组成
- spring 七大模块
1.4 拓展
- SpringBoot
- 一个快速开发的脚手架。
- 基于SpringBoot可以快速的开发单个微服务
- 约定大于配置
- SpringCloud
- 基于SpringBoot实现
弊端:配置十分繁琐
2. IOC理论推导
1.UserDao接口
2.UserDaoImpl实现类
3.UserService业务接口
4.UserServiceImplement
在之前的业务中,用户需求可能影响原来代码,需要根据用户需求修改原代码
使用一个set接口实现注入
//业务层只做一件事,调用dao层去查
private UserDao userDao;
//利用set进行动态实现值的注入!
public UserDao getUserDao() {
return userDao;
}
- 之前,程序是主动创建对象,控制权在程序员手上
- 使用了set注入之后,程序不在具有主动性,而是变成了被动的接受对象!
- (IOC控制反转)主动权变化
这种思想,本质上解决了问题,程序员不用再去管理创建对象了。系统的耦合性大大降低,可以更加注重在业务的实现上。这是IOC原型
**IOC本质:**IOC是一种设计思想,DI(依赖注入)是实现IOC的一种方法。没有IOC的程序中,我们使用面向对象编程,对象的创建与对象的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,即获得依赖对象的方式反转了。
IOC是spring框架核心内容,可以使用Xml配置、注解,新版spring也可以零配置实现IOC。
spring容器在初始化时,先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从IOC容器中取出需要的对象
采用xml方式配置bean的时候,bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,bean的定义信息直接以注解的形式定义在实现类中,从而达到零配置的目的。
控制反转是一种通过描述(xml或注解)并通过第三方去生产或获取特定对象的方式,在spring中实现控制反转的是IOC容器,其实现方法是依赖注入
控制:谁来控制对象的创建,传统应用程序的对象由程序本身控制,使用spring后,对象有spring创建
反转:程序本身不在创建对象,而变成被动的接收对象
依赖注入:利用set方法进行注入
3. HelloSpring
-
架构
-
Hello
package com.banyuan.pojo;
public class Hello {
private String str;
@Override
public String toString() {
return "hello{" +
"str='" + str + '\'' +
'}';
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
- 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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 使用spring来创建对象,在spring中这些都成为bean
类型 变量名 = new 类型();
Hello hello = new hello();
id = 变量名
class = new的对象;
property相当于给对象中的属性设置一个值
bean = 对象 new Hello();
-->
<bean id="hello" class="com.banyuan.pojo.Hello">
<!--
ref:引用spring容器中创建好的对象
value:具体的值,基本数据类型!
-->
<property name="str" value="Spring"></property>
</bean>
</beans>
- MyTest
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.banyuan.pojo.Hello;
public class MyTest {
public static void main(String[] args) {
//获取spring的上下文对象!
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//我们的对象现在都在spring中管理了,要使用直接去里面取出来
//没有new hello 直接取出来
Hello hello = (Hello) context.getBean("hello");
String s = hello.toString();
System.out.println(s);
}
}
4.IOC创建对象方式
1.下标赋值
<bean id="user" class="com.banyuan.pojo.User">
<constructor-arg index="0" value="hhh"></constructor-arg>
</bean>
2.类型赋值
<!--不建议使用-->
<bean id="user" class="com.banyuan.pojo.User">
<!--基本类型可以直接用-->
<!-- <constructor-arg type="int" value="ggg"></constructor-arg>-->
<constructor-arg type="java.lang.String" value="ggg"></constructor-arg>
</bean>
3.参数名赋值
<bean id="user" class="com.banyuan.pojo.User">
<constructor-arg name="name" value="ggg"></constructor-arg>
</bean>
**总结:**在配置文件加载的时候,容器中管理的对象就已经初始化了
5. spring配置
5.1 别名
<!-- user -> NewUser -->
<alias name="user" alias="NewUser"></alias>
5.2 Bean的配置
<!--
id:bean 的唯一标识符,也就是相当于对象名
class: bean对象所对应的全限定名:包名+类型
name: 别名 更高级,可以同时取多个别名
-->
<bean id="userT" class="com.banyuan.pojo.UserT" name="newUserT,u2,u3;u4">
<property name="name" value="GGG"></property>
</bean>
5.3 import
一般用于团队开发使用,可以将多个配置文件,导入合并为一个
假设有多个人开发,负责不同的开发,不同的类需要注册在不同的bean中,我们可以利用import将所有的beans.xml合并。使用的时候直接使用总的配置
- applicationContext.xml
- beans.xml
- beans2.xml
- beans3.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">
<import resource="beans.xml"></import>
<import resource="beans2.xml"></import>
<import resource="beans3.xml"></import>
</beans>
6.DI依赖注入
6.1 构造器注入
见ioc创建对象方式
6.2 set注入[重点]
- 依赖注入:set注入
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性由容器来注入
【环境搭建】
- Address
package com.banyuan.pojo;
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
- Student
package com.banyuan.pojo;
import java.util.*;
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbies;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
public Student() {
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address.toString() +
", books=" + Arrays.toString(books) +
", hobbies=" + hobbies +
", card=" + card +
", games=" + games +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
}
- 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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="ades" class="com.banyuan.pojo.Address">
<property name="address" value="南京"></property>
</bean>
<bean id="student" class="com.banyuan.pojo.Student">
<!--第一种,普通值(string)注入,value-->
<property name="name" value="ghc"></property>
<!--第二种,bean注入,ref-->
<property name="address" ref="ades"></property>
<!--第三种,数组注入,ref-->
<property name="books">
<array>
<value>三国演义</value>
<value>西游记</value>
<value>水浒站</value>
<value>红楼梦</value>
</array>
</property>
<!--第四种,list注入-->
<property name="hobbies">
<list>
<value>敲代码</value>
<value>听歌</value>
</list>
</property>
<!--第五种,map-->
<property name="card">
<map>
<entry key="身份证" value="1111 1111 "></entry>
<entry key="银行卡" value="2222 2222 "></entry>
</map>
</property>
<!--第六种,set-->
<property name="games">
<set>
<value>LOL</value>
<value>CS:GO</value>
</set>
</property>
<!--第七种,null注入-->
<property name="wife">
<null></null>
</property>
<!--第八种,properties特殊类型
key = value
-->
<property name="info">
<props>
<prop key="学号">20183309</prop>
<prop key="性别">男</prop>
</props>
</property>
</bean>
</beans>
- MyTest
import com.banyuan.pojo.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = context.getBean("student",Student.class);
System.out.println(student);
/**
* Student{name='ghc', address=Address{address='南京'},
* books=[三国演义, 西游记, 水浒站, 红楼梦],
* hobbies=[敲代码, 听歌],
* card={身份证=1111 1111 , 银行卡=2222 2222 },
* games=[LOL, CS:GO], wife='null',
* info={学号=20183309, 性别=男}}
*/
}
}
6.3 其他方式注入
6.3.1c命名和p命名空间注入
p对应了set注入,c对应了构造器注入
- User
public class User {
private String name;
private int age;
public User() {
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
}
- userBeans.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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
头文件加入:xmlns:p="http://www.springframework.org/schema/p"
-->
<!--p命名空间注入,可以直接注入属性的值:property-->
<bean id="user" class="com.banyuan.pojo.User" p:name="ghc" p:age="18"></bean>
<!--
头文件加入:xmlns:c="http://www.springframework.org/schema/c"
-->
<!--c命名空间注入,可以通过构造器注入,construct-args-->
<bean id="user" class="com.banyuan.pojo.User" c:name="ghc" c:age="20"></bean>
</beans>
- MyTest
public class MyTest2 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("userBeans.xml");
User user1 = context.getBean("user",User.class);
User user2 = context.getBean("user2",User.class);
System.out.println(user1);
System.out.println(user2);
}
}
/**
* User{name='ghc', age=18}
* User{name='ghc', age=20}
*/
注意点:p命名和c命名空间不能直接使用,要导入xml约束
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
6.4. Bean的作用域
1.singleton单例模式(spring默认机制)
- 所有人共享一个对象
<bean id="user2" class="com.banyuan.pojo.User" c:name="ghc" c:age="20" scope="singleton"></bean>
System.out.println(user1 == user2);//True
2.propertype原型模式
- 每一个bean都是一个单独对象,每次从容器中get的时候,都会产生一个新对象
<bean id="user2" class="com.banyuan.pojo.User" c:name="ghc" c:age="20" scope="prototype"></bean>
System.out.println(user1 == user2);//Flase
3.其余的request,session,application这些只能在web开发中使用到
7. Bean的自动装配
- 自动装配是spring满足bean依赖的一种方式
- spring会在上下文中自动寻找,并自动给bean装配属性
在spring中有三种自动装配的方式
1.在xml中显示的配置
2.在java中显示的配置
3.隐式的自动装配bean【重要】
7.1 测试
1.环境搭建:一个人有两个宠物
<bean id="people" class="com.banyuan.pojo.People>
<property name="name" value="ghc"></property>
<property name="dog" ref="dog"></property>
<property name="cat" ref="cat"></property>
</bean>
7.2 ByName自动装配
byName:会自动在容器上下文中查找,和自己对象set方法后面的值对象的bean id
<bean id="people" class="com.banyuan.pojo.People" autowire="byName">
<property name="name" value="ghc"></property>
</bean>
7.3 ByType自动装配
byType:会自动在容器上下文中查找,和自己对象属性类型相同bean
<bean id="people" class="com.banyuan.pojo.People" autowire="byType">
<property name="name" value="ghc"></property>
</bean>
小结:
- byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致。
- byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致。
7.4 使用注解实现自动装配
7.4.1 使用注解须知:
1.导入约束
2.配置注解的支持: <context:annotation-config/>
<?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/>
</beans>
7.4.2 @Autowired
直接在属性上使用即可
使用Autowired可以不用编写set方法,前提是自动装配的属性在IOC(spring)容器中存在,且符合名字byName!
科普:
@NUllable 字段标记了这个注解,说明这个字段可以为null
@Autowired(required = false) 如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空,默认为true
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解完成的时候,可以使用@Qualifier(value = “xxx”)去配合@Autowired的使用,指定一个唯一的bean对象注入
@Autowired
@Qualifier(value = "cat1")
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
<context:annotation-config/>
<bean id="cat1" class="com.banyuan.pojo.Cat"></bean>
<bean id="cat2" class="com.banyuan.pojo.Cat"></bean>
<bean id="dog1" class="com.banyuan.pojo.Dog"></bean>
<bean id="dog2" class="com.banyuan.pojo.Dog"></bean>
7.4.3 @Resouece
@Resource
private Cat cat;
@Resource
private Dog dog;
小结:
@Autowired和@Resouece区别:
-
都是用来自动装配,都可以放在属性字段上
-
@Autowired 通过byType的方式实现,必须要求对象存在
-
@Resouece 默认通过byName方式实现,如果找不到名字,则通过byType方式实现,如果都找不到就报错
-
执行顺序不同
8. 使用注解开发
spring4之后,要使用注解开发,必须要保证AOP的包导入了
-
bean
@Component:组件,放在类上,说明这个类被spring管理了,就是bean
-
属性如何注入
@Component public class User { public String name ; @Value("ghc") public void setName(String name) { this.name = name; } }
-
衍生的注解
@Component有几个衍生注解,在web开发中,会按MVC三层架构分层- dao -> @Repository
- service -> @Service
- controller -> @Controller
这四个注解功能都是一样的,都是代表将某个类注册到Spring中
-
自动装配置
注解说明:- @Autowired:自动装配通过类型,名字
如果Autowired不能唯一装配上属性,则需要通过@Qualifier(Value = “xxx”) - @Nullable:字段标记了这个注解,说明这个字段可以为null
- @Resource:自动装配通过名字,类型
- @Autowired:自动装配通过类型,名字
-
作用域
@Scope
springIoc容器中的一个作用域,在 Spring IoC 容器中具有以下几种作用域:基本作用域singleton(单例)、prototype(多例),Web 作用域(reqeust、session、globalsession),自定义作用域 -
小结
xml与注解:- xml更加万能,适用于任何场合,维护简单方便
- 注解 不是自己的类使用不了,维护相对复杂
最佳实践:
xml用来管理bean 注解只负责属性的注入
使用的过程中,只需要注意:必须让注解生效,就需要开启注解支持
<!--开启注解支持--> <context:annotation-config/> <!--指定要扫描的包--> <context:component-scan base-package="com.banyuan.pojo"></context:component-scan>
9. 使用JavaConfig实现配置
现在要完全不使用spring的xml配置,全部交给java来做
JavaConfig是spring的一个子项目
【环境搭建】
- MyConfig
package com.banyuan.config;
import com.banyuan.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
//@Configuration代表这是一个配置类。就和beans.xml是一样的
@Configuration//这个也会被spring容器托管,注册到容器中,因为它本来就是一个@Component
@ComponentScan("com.banyuan.pojo")//扫描包
@Import(MyConfig2.class) //引入类的class,相当于导入
public class MyConfig {
/* 注册一个bean,就相当于写的一个bean标签
这个方法的名字就相当于bean标签的id属性,
返回值就相当于class属性 */
@Bean
public User getUser(){
return new User();//返回要注入到bean的对象
}
}
- User
package com.banyuan.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//这个注解说明,这个类被spring接管,注册到了容器中
@Component
public class User {
private String name;
@Value("ghc")//属性注入值
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
- MyTest
import com.banyuan.config.MyConfig;
import com.banyuan.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest {
@Test
public void test1(){
//如果完全使用配置类方式,就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User getUser = context.getBean("getUser",User.class);
System.out.println(getUser);
}
}
// User{name='ghc'}
10. 代理模式
SpringAOP的底层!
代理模式分类:
- 静态代理
- 动态代理
10.1 静态代理
角色分析:
- 抽象角色:一般会使用接口或抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理后一般会做附属操作
- 客户:访问代理对象的人
代码步骤:
1.接口
//租房接口
public interface Rent {
public 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 void setHost(){
System.out.println("中介带你看房");
}
//签合同
public void contract(){
System.out.println("签合同");
}
@Override
public void rent() {
setHost();
host.rent();
contract();
}
public Proxy(Host host) {
this.host = host;
}
public Proxy() {
}
}
4.客户端访问代理角色
//租房的人
public class Client {
public static void main(String[] args) {
Host host = new Host();
//代理,代理角色一般会有附属操作
Proxy proxy = new Proxy(host);
//不用面对房东
proxy.rent();
}
}
/*
中介带你看房
房东要出租房子
签合同*/
优点:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共业务
- 公共业务也就交给了代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率遍地
10.2 动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口—jdk动态代理
- 基于类 — cglib
- java字节码实现:javasist
需要了解两个类:Proxy(代理),InnovationHandler(调用处理程序)
动态代理优点:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共业务
- 公共业务也就交给了代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理可以代理多个类,只要实现一个接口就行
ProxyInvocationHandler
//用这个类,自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
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+"方法");
}
}
Client
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userService = new UserServiceImpl();
//代理角色,不存在
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userService);//设置要代理的对象
//动态生成代理类
UserService proxy = (UserService) pih.getProxy();
proxy.delete();
}
}
/*
执行了delete方法
删除一个用户*/
11.AOP
11.1 什么是AOP
面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
11.2 AOP在spring中的作用
提供声明式事务;允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点.如日志,安全,缓存,事务等等
- 切面(ASPECT):横切关注点被模块化的特殊对象。即,它是一个类。
- 通知(Advice) :切面必须要完成的工作.即,它是类中的一个方法。
- 目标(Target):被通知对象。
- 代理(Proxy):向目标对象应用通知之后创建的对象。
- 切入点(PointCut):切面通知执行的‘地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。
11.3 使用Spring实现AOP
先导入依赖包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
**方式一:**使用Spring的API接口【主要SpringAPI接口实现】
BeforeLog
public class BeforeLog implements MethodBeforeAdvice {
//method:要执行的目标对象的方法 object[]:参数 Object:目标对象
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
applicationContext.xml
<!--注册bean-->
<bean id="userService" class="com.banyaun.service.UserServiceImpl"></bean>
<bean id="beforeLog" class="com.banyaun.log.BeforeLog"></bean>
<bean id="afterLog" class="com.banyaun.log.AfterLog"></bean>
<!--方式一:使用原生的spring api接口-->
<!--配置aop-->
<aop:config>
<!--切入点。expression:(要执行的位置)表达式-->
<aop:pointcut id="pointcut" expression="execution(* com.banyuan.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"></aop:advisor>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
MyTest
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
**方式二:**自定义类使用AOP【主要是切面定义】
DiyPointCut
public class DiyPointCut {
public void before(){
System.out.println("==========before==========");
}
public void after(){
System.out.println("==========after==========");
}
}
applicationContext.xml
<!--方式二:自定义类-->
<bean id="diy" class="com.banyaun.diy.DiyPointCut"></bean>
<aop:config>
<!--自定义切面,ref 要引用到的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.banyuan.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="point"></aop:before>
<aop:after method="after" pointcut-ref="point"></aop:after>
</aop:aspect>
</aop:config>
**方式三:**使用注解实现
//使用注解方式实现AOP
@Aspect //标注这个类是一个切面
public class AnnoPointCut {
@Before("execution(* com.banyaun.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("before");
}
@After("execution(* com.banyaun.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("after");
}
/**
* 在环绕增强前,我们可以给定一个参数,代表我们要获取处理切入的点
*/
@Around("execution(* com.banyaun.service.UserServiceImpl.*(..))")
//切入点
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前");
//执行方法
Object proceed = joinPoint.proceed();
System.out.println("环绕后");
}
}
/**
* 环绕前
* before
* 增加了一个用户
* 环绕后
* after
*/
<!--方式三:注解-->
<bean id="annopointcut" class="com.banyaun.diy.AnnoPointCut"></bean>
<!-- 开启注解 默认jdk(proxy-target-class="false") cglib(proxy-target-class="true")-->
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
12. 整合Mybatis
步骤:
-
导入相关jar包
Junit、mybatis、mysql数据库、spring相关的、aop、mybatis-spring
-
编写配置文件
-
测试
12.1 回忆mybatis
1.编写实体类
2.编写核心配置文件
3.编写接口
4.编写Mapper.xml
5.测试
12.2 Mybatis-spring
UserMapper
public interface UserMapper {
public List<User> findAll();
public User findUserById(Integer id);
public void insert(User user);
public void update(User user);
public void deleteById(Integer id);
}
User
@Data
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private String password;
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.banyuan.dao.UserMapper">
<!-- 配置查询所有操作 -->
<select id="findAll" parameterType="java.util.HashMap" resultType="user">
select *
from user
</select>
<select id="findUserById" parameterType="Integer" resultType="user">
select * from user where id=#{id}
</select>
<!-- 注意:返回值,insert是没有的,默认是返回插入的条数-->
<insert id="insert" parameterType="user" useGeneratedKeys="true" keyProperty="id">
insert into user (username, birthday, sex, address)
values (#{username}, #{birthday}, #{sex}, #{address})
</insert>
<insert id="update" parameterType="user">
update user
set username=#{username},
sex = #{sex},
address = #{address},
birthday = #{birthday}
where id = #{id}
</insert>
<insert id="deleteById" parameterType="user">
delete
from user
where id = #{id}
</insert>
</mapper>
MyTest
public class MyTest {
InputStream is = null;
SqlSessionFactoryBuilder builder = null;
SqlSessionFactory factory = null;
SqlSession session = null;
UserMapper userMapper = null;
@Before
public void before() throws IOException {
//1.读取配置文件
is = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建 SqlSessionFactory 的构建者对象
builder = new SqlSessionFactoryBuilder();
//3.使用构建者创建工厂对象 SqlSessionFactory
factory = builder.build(is);
//4.使用 SqlSessionFactory 生产 SqlSession 对象
session = factory.openSession();
// session = factory.openSession(true);
//5.使用 SqlSession 创建 dao 接口的代理对象
userMapper = session.getMapper(UserMapper.class);
}
@Test
public void findAll(){
//6.使用代理对象执行查询所有方法
List<User> users =userMapper.findAll();
for (User user : users) {
System.out.println(user);
}
}
@Test
public void findUserById(){
User user = userMapper.findUserById(1);
System.out.println(user);
}
@Test
public void insert() {
User user = new User();
user.setUsername("李四");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("苏州");
userMapper.insert(user);
System.out.println(user.getId() + "user");
}
@Test
public void update() {
// User user = new User();
// user.setId(4);
// user.setUsername("王五");
// user.setSex("女");
// user.setAddress("上海");
// user.setBirthday(new Date());
User user = userMapper.findUserById(3);
user.setUsername("王五");
userMapper.update(user);
}
@Test
public void delete() {
userMapper.deleteById(4);
}
@After
public void after() throws IOException {
//7.释放资源
session.commit();
session.close();
is.close();
}
}
13 声明式事务
13.1 回顾事务
- 要么都成功,要么都失败
- 确保完整性和一致性
事务四大特性有:
- 原子性:事务下的操作要么全部成功,要么全部回滚
- 一致性:事务前后数据库都要处于一致状态(转账前后两个账户的总金额一致)
- 持久性:事务对数据的修改是持久的(即使数据库文件损坏也可通过日志等恢复)
- 隔离性:多事务并发访问时可能会造成数据安全问题,需要把事务隔离起来(加锁)
不同事务隔离级别的并发访问性能和数据安全性都不同,常见数据安全问题如下:
- 脏读:事务A读到了事务B未提交的数据(即使事务没有提交,SQL更新语句也会修改表)
- 不可重复读:事务A前后两次读取的同一行数据不同(期间事务B修改了这行数据并提交)
- 幻读:事务A删除表中全部数据后再次查询发现还有数据没删除(期间事务B插入新数据)
有四种数据库隔离级别:
read_uncommitted:读未提交 完全没有隔离,会出现脏读、不可重复读和幻读
read_committed:读提交 简单隔离,不会出现脏读(把两个事务做了一定的隔离)。 oracle
repeatable_read:不可重复度 中度隔离,不会出现脏读、不可重复读。 mysql的默认级别
serializable: 完全隔离,不会出现脏读、不可重复读和幻读
13.2 事务管理
-
声明式事务:AOP
-
编程式事务:需要在代码中,进行事务的管理
13.2.1 事务传播行为
required:如果当前有事务,就使用当前的事务,如果没有,就开启新事务
supports:如果当前有事务,就使用当前事务,如果没有,则不开启事务
requires_new:如果当前有事务,则挂起当前事务,并开启新事务;否则直接开启新事务
not_supported:如果当前有事务,则挂起当前事务
nested:如果当前有事务,则开启一个嵌套事务;如果当前没有事务,开启新事务
mandatory:如果当前没有事务,则抛出异常
never:如果当前有事务,则抛出异常
<!--第一步:要开启事务处理,在spring的配置文件中创建一个DataSourceTranslationManager对象-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DataSourceTranslationManager">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>
<!--集合AOP 实现事务织入
配置事务的类
-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给方法配置事务-->
<!--配置事务的传播特性:new-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"></tx:method>
<tx:method name="select" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txAop" expression="execution(com.banyuan.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txAop"></aop:advisor>
</aop:config>