狂神说Spring
1.简介
SSH: Struct2 + Spring + Hibernate
SSM: SpringMVC + Spring + Mybatis
官网:https://spring.io/projects/spring-framework
下载地址:https://repo.spring.io/release/org/springframework/spring/
github:https://github.com/spring-projects/spring-framework
1.1 依赖
Spring web MVC
<!-- Spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<!-- Spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
1.2 优点:
- Spring是一个开源的免费框架(容器)
- Spring是一个轻量级的,非入侵式的框架
- 控制反转(IOC) , 面向切面编程(AOP)
- 支持事务的处理, 对框架整合的支持!
总结:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!
1.3 组成
1.4 拓展
基于Spring的开发
- Spring Boot
- 一个快速开发的脚手架
- 基于Spring boot 可以快速的开发单个微服务
- 约定大于配置
- Spring cloud
- Spring cloud 是基于Spring boot实现的
弊端:发展了太久之后违背了原来的了理念! 配置十分繁琐, 江湖人称"配置地域"
2.IOC理论推导
service (业务层)就一个功能:调dao去查用户实际调用的是业务层,dao层他们不接触!
Beans.xml,正确命名:
bean.xml创建之后 IDEA上方有一行黄色 点击就可以出现小叶子
<?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">
<!-- services -->
<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for services go here -->
</beans>
用户
ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");
首先dao层创建一个接口
public interface UserDao {
public void getUser();
}
然后创建他的实现类
public class UserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("hello word");
}
}
如果还需要增加业务 再增加一个实现类
public class MysqlDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("我是Mysql实现类");
}
}
service层 接口
service就是一个老大 自己的任务 安排小弟dao层去做
public interface UserService {
public void getUser();
}
service实现类
每增加一个业务 都需要在业务层 重新生产一个dao层对象
public class UserServiceImpl implements UserService{
// private UserDaoImpl userDao = new UserDaoImpl();
private MysqlDaoImpl userDao = new MysqlDaoImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
用户(测试)
public class Mytest {
public static void main(String[] args) {
//用户实际调用的是业务层,dao层他们不接触!
UserServiceImpl userService = new UserServiceImpl();
userService.getUser();
}
}
只要在service实现类中添加set方法,利用set进行动态实现值的注入
private UserDao userDao;
//利用set进行动态实现值的注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
用户 —用什么–自己调什么
public class Mytest {
public static void main(String[] args) {
//用户实际调用的是业务层,dao层他们不接触!
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(new UserDaoImpl());
// userService.setUserDao(new MysqlDaoImpl());
userService.getUser();
}
}
-
之前,程序是主动创建的! 控制权在程序员手上!
-
使用set注入后,程序不在具有主动性了,而是变成了被动的接受对象!
这种思想,从本质上解决了问题,我们程序员不用再去管理对象的创建了.系统的耦合性大大减低了,可以更加专注在业务的实现上!这是IOC的原型.
之前:主动权在程序员
现在:主动权在用户
IOC本质
控制反转IOC , 是一种设计思想 , DI(依赖注入)是实现IOC的一种方法,
控制反转是一种通过描述(xml或注解) 并通过第三方去生产或获取特定对象的方式. 在Spring中实现控制反转的是IOC容器, 其实现方法是依赖注入(DI)
3.HelloSpring
实体类—beans.xml—测试
Hello实体类
public class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Hello{" +
"name='" + name + '\'' +
'}';
}
}
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-->
<bean id="hello" class="com.codeyuaiiiao.pojo.Hello">
<property name="name" value="Spring"></property>
</bean>
</beans>
测试
public class Mytest {
public static void main(String[] args) {
//获取Spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Hello hello = (Hello)context.getBean("hello");
System.out.println(hello.toString());
}
}
beans.xml中
ref:引用Spring容器中创建好的对象
value:具体的值,基本数据类型!
上一个例子改变
dao层不变
service层不变
只增加一个beans.xml
<bean id="userImpl" class="com.codefuheng.dao.UserDaoImpl"/>
<bean id="mysqlImpl" class="com.codefuheng.dao.MysqlDaoImpl"/>
<bean id="oracleImpl" class="com.codefuheng.dao.OracleDaoImpl"/>
<bean id="UserServiceImpl" class="com.codefuheng.service.UserServiceImpl">
<property name="userDao" ref="oracleImpl"></property>
</bean>
测试
public static void main(String[] args) {
//获取ApplicationContext 拿到Spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
//需要什么 直接get什么
UserServiceImpl userServiceImpl = (UserServiceImpl)context.getBean("UserServiceImpl");
userServiceImpl.getUser();
}
4.IOC创建对象的方法
-
使用无参构造创建对象,默认
-
假设我们要使用有参构造创建对象.
-
下标赋值
<!-- 第一种:通过下标创建--> <bean id="User" class="com.codefuheng.pojo.User"> <constructor-arg index="0" value="java"></constructor-arg> </bean>
-
通过类型创建
<!-- 第二种: 通过类型创建,不建议使用--> <bean id="User" class="com.codefuheng.pojo.User"> <constructor-arg type="java.lang.String" value="java"/> </bean>
-
参数名
<!-- 第三种: 直接通过参数名来设置(推荐)--> <bean id="user" class="com.codefuheng.pojo.User"> <constructor-arg name="name" value="java"/> </bean>
-
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了!
5.Spring配置
5.1 别名
<!--别名:如果添加了别名 我们也可以用别名来创建对象-->
<alias name="user" alias="user2"></alias>
5.2 Bean的配置
<!-- id: Bean的唯一标识符, 也就是我们学到的对象名
class: bean对象所对应的权限定名: 包名+类名
name: 也是别名,而且name可以同时取多个别名
name的别名 可以用空格 , ; 多种方式分割
-->
<bean id="user" class="com.codefuheng.pojo.User" name="user2 user3,user4;user5">
<constructor-arg name="name" value="java"/>
</bean>
5.3 import
applicationContext.xml
<import resource="beans.xml"></import>
<import resource="beans2.xml"></import>
<import resource="beans3.xml"></import>
使用的时候只需要使用总的xml就可以 applicationContext.xml
6. 依赖注入(DI)
概念
- 依赖注入(Dependency Injection,DI)。
- 依赖 : 指Bean对象的创建依赖于容器 。 Bean对象的依赖资源 。
- 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 。
6.1 构造器注入
之前的案例已经讲过了
6.2 set方式注入 [重点]
[环境搭建]
1.复杂类型
Address
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 + '\'' +
'}';
}
}
2.真实测试对象
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;
}
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">
<!--第一种:普通值注入 value-->
<bean id="Student" class="com.codefuheng.pojo.Student">
<property name="name" value="付恒"/>
</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.getAddress());
}
}
不同类型的注入方式(array map set list…)
<bean id="address" class="com.codefuheng.pojo.Address">
<property name="address" value="南京"/>
</bean>
<bean id="student" class="com.codefuheng.pojo.Student">
<!--第一种:普通值注入 value-->
<property name="name" value="付恒"/>
<!--第二种:Bean注入 ref-->
<property name="address" ref="address"/>
<!--第三种: 数组-->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
<value>三国演义</value>
</array>
</property>
<!--第四种:list-->
<property name="hobbies">
<list>
<value>打游戏</value>
<value>听歌</value>
<value>看电影</value>
</list>
</property>
<!--第五种:Map-->
<property name="card">
<map>
<entry key="身份证" value="15313234652341"/>
<entry key="银行卡" value="4564653141465354"/>
</map>
</property>
<!--第六种:set-->
<property name="games">
<set>
<value>王者荣耀alue>
<value>斗地主</value>
<value>狼人杀/value>
</set>
</property>
<!--null-->
<property name="wife">
<null/>
</property>
<!--Properties-->
<property name="info">
<props>
<prop key="姓名">付恒</prop>
<prop key="学号">161302105</prop>
</props>
</property>
</bean>
6.3 拓展方式注入
我们可以使用p命名空间和c命名空间来注入
官方位置
使用
测试:
后面加 类名.class 就不用强转了
注意点:p命名空间和c命名空间不能直接使用,需要导入xml约束
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
6.4 Bean的作用域(了解)
在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象 .
几种作用域中,request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境。
Singleton
当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:
<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">
测试:
@Test
public void test03(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
System.out.println(user==user2);
}
Prototype
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:
<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>
或者
<bean id="account" class="com.foo.DefaultAccount" singleton="false"/>
Request
当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
<bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>
针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。
Session
当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。
7.Bean的自动装配
- 自动装配是Spring满足bean依赖的一种方式!
- Spring会在上下文中自动寻找, 并自动给bean装配属性!
在Spring中有三种装配方式
- 在xml中显示装配
- 在java中显示装配
- 隐式的自动装配bean [重要]
7.1.测试
1.环境搭建
- 一人 (人有两宠)
- 一狗
- 一猫
7.2 byName自动装配
byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid
<bean id="cat" class="com.codefuheng.pojo.Cat"></bean>
<bean id="dog" class="com.codefuheng.pojo.Dog"></bean>
<!-- byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid-->
<bean id="people" class="com.codefuheng.pojo.People" autowire="byName">
<property name="name" value="付恒"></property>
</bean>
7.3 byType 自动装配
<bean class="com.codefuheng.pojo.Cat"></bean>
<bean class="com.codefuheng.pojo.Dog"></bean>
<!-- byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean-->
<bean id="people" class="com.codefuheng.pojo.People" autowire="byType">
<property name="name" value="付恒"></property>
</bean>
小结:
- byName的时候 , 需要保证所有bean的id唯一 ,并且这个bean需要和自动注入的属性的set方法的值一致
- byType的时候 , 需要保证所有bean的class唯一 ,并且这个bean需要和自动注入的属性的类型一致
7.4 使用注解 实现自动转配 (重点)
jdk1.5支持注解------Spring 2.5 支持注解
使用注解须知:
- 导入约束 : context约束
- 配置注解的支持 : 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
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.codefuheng"/>
<!--注解生效-->
<context:annotation-config/>
</beans>
@Autowired
直接在属性上使用即可! 也可以在set方式上使用!
使用@Autowired 我们可以不让用编写set方法了 , 前提是你这个自动装配的属性在IOC(Spring) 容器中存在 , 且符合名字 byname !
科普:
@Nullable
@Nullable 字段标记了这个注解,说明这个字段可以为null;
@Autowired ( required = false)
如果显示的定义了@Autowired的required的属性为false,说明这个对象可以为nulll.否则不允许为空
实体类
public class People {
private String name;
@Autowired
private Dog dog;
@Autowired
private Cat cat;
}
@Resource
可以指定名字
public class People {
private String name;
@Resource(name = "dog22")
private Dog dog;
@Resource(name = "cat22")
private Cat cat;
}
beans.xml中
<bean id="cat" class="com.codefuheng.pojo.Cat"/>
<bean id="cat22" class="com.codefuheng.pojo.Cat"/>
<bean id="dog" class="com.codefuheng.pojo.Dog"/>
<bean id="dog22" class="com.codefuheng.pojo.Dog"/>
<bean id="people" class="com.codefuheng.pojo.People" />
小结:
@Resource 和 @Autowired 的区别
- 都是用来装配的, 都可以放在属性字段上
- @ Autowired 默认通过byType的方式实现, 而且必须要求这个对象存在 !如果找不到名字 , 则通过byname实现 [常用]
- @ Resource 默认通过byname的方式实现, 如果找不到名字 , 则通过byType实现! 如果两个都找不到的情况下, 就报错
- 执行顺序不同 : @ Autowired 通过byType 的方式实现. @Resource 默认通过byname的方式实现.
8.使用注解开发
applicationContext.xml
<!--指定要扫描的包, 这个包下的注解就会生效-->
<Context:component-scan base-package="com.codefuheng.pojo"/>
<Context:annotation-config/>
在Spring4之后 , 要使用注解开发, 必须保证 aop的包导入了
使用注解需要导入context约束, 增加注解的支持!
-
bean
-
属性如何注入
/** * 等价于:<bean id="user" class="com.codefuheng.pojo.User"/> * @Component 组件 */ @Component public class User { /** * 相当于<property name="name" value = "付恒" */ @Value("付恒") public String name; }
-
衍生的注解
- @Component 有几个衍生注解 , 我们在web开发中, 会按照mvc三层架构 分层!
- dao [@Repository]
- service [@Service]
- controller [@Controller]
- 这个四个注解功能都是一样的 , 都是代表某个类注册到Spring中, 装配Bean
- 自动装配
- @ Autowired : 自动装配通过类型,名字;
- 如果@Autowired 不能唯一自动装配上属性,则需要通过@Qualifier(value=“xxx”)
- @Nullable : 字段标记了这个注解 , 说明这个字段可以为null;
- @Resource : 自动装配通过名字, 类型.
-
作用域
@scope
- singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
- prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
@Controller("user") @Scope("prototype") public class User { @Value("付恒") public String name; }
-
小结
xml与注解:
- xml更加万能 , 适用于任何场合! 维护简单方便
- 注解不是自己类使用不了 , 维护相对复杂!
xml 与 注解最佳实践:
-
xml 用来管理bean;
-
注解只负责完成属性的注入;
-
我们在使用的过程中 , 只需要注意一个问题 : 必须让注解生效 , 就需要开启注解的支持.
-
<!--指定要扫描的包, 这个包下的注解就会生效--> <Context:component-scan base-package="com.codefuheng.pojo"/> <!--加载注解--> <Context:annotation-config/>
注解说明
- @ Autowired : 自动装配通过类型,名字;
- 如果@Autowired 不能唯一自动装配上属性,则需要通过@Qualifier(value=“xxx”)
- @Nullable : 字段标记了这个注解 , 说明这个字段可以为null;
- @Resource : 自动装配通过名字, 类型.
- @component : 组件 , 放在类上 , 说明这个类被Spring 管理了 , 就是bean !
9.使用java的方式配置Spring
实体类
public class User {
private String name;
public String getName() {
return name;
}
@Value("付恒")
public void setName(String name) {
this.name = name;
}
}
配置类
package com.codefuheng.config;
import com.codefuheng.pojo.User;
import org.springframework.context.annotation.*;
/**
* @author:fuheng
*/
@Configuration
@ComponentScan("com.codefuheng.pojo")
@Import(config2.class)
public class config {
/**
* 注册一个Bean,就相当于我们之前写的一个bean标签
* 这个方法的名字,就相当于bean标签中的id属性
* 这个方法的返回值,就相当于bean标签中的class属性
* @return
*/
@Bean
public User getUser(){
//就是返回要注入到bean中的对象
return new User();
}
}
测试类
public class Mytest {
@Test
public void getUser(){
ApplicationContext Context = new AnnotationConfigApplicationContext(config.class);
User getUser = Context.getBean("getUser", User.class);
System.out.println(getUser.getName());
}
}
- 这种纯java的配置方式,在Spring Boot中随处可见
10.代理模式
为什么要学习代理模式? 因为这就是SpringAOP的底层! [SpringAOP 和SpringMVC]面试必考
代理模式的分类:
- 静态代理
- 动态代理
10.1 静态代理
角色分析:
- 抽象角色 : 一般会使用接口或者抽象类来解决
- 真实角色: 被代理的角色
- 代理角色: 代理真实角色 , 代理真实角色后, 我们一般会做一些附属操作.
- 客户 : 访问代理对象的人
代码步骤:
-
接口
/** * @author: fuheng * 租房 */ public interface Rent { public void rentHouse(); }
-
真实角色
/** * @author: fuheng * 房东 */ public class Host implements Rent{ public void rentHouse() { System.out.println("房东出租房子"); } }
-
代理角色
/** * @author: fuheng * 中介代理 * 中介实现接口 出租房子 * 中介 每次都能代理一个房东 出租房子(每次传进一个房东) */ public class Proxy implements Rent{ private Host host; //租房 public void rentHouse() { host.rentHouse(); } public Proxy(Host host) { this.host = host; } }
-
客户端访问代理角色
/**
* @author: fuheng
* 客户 我租房子
*/
public class Client {
public static void main(String[] args) {
//new 一个房东 房东要租房子
Host host = new Host();
//代理 , 中介帮房东租房子, 但是呢? 代理一般会有一些附属操作
Proxy proxy = new Proxy(host);
//你不用面对房东, 直接找中介租房即可!
proxy.rentHouse();
}
}
代理模式的好处:
- 可以使真实角色的操作更加纯粹 ! 不用去关注一些公共的业务
- 公共业务交给代理角色 ! 实现了业务的分工!
- 公共业务发生拓展的时候 ,方便集中管理!
缺点:
- 一个真实角色就会产生一个代理角色 ; 代码量会翻倍----开发效率会变低—
10.2 动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的 , 不是我们直接写好的!
- 动态代理分为两大类: 基于接口的动态代理 , 基于类的动态代理
- 基于接口-------JDK动态代理 [我们在这里使用]
- 基于类 : cglib
- java字节码实现 : javasist
需要了解两个类: Proxy : 生成动态代理实例的 , invocationHandler: 调用处理程序并返回一个结果的
invocationHandler
1.接口
/**
* @author: fuheng
* 租房
*/
public interface Rent {
public void rentHouse();
}
2.实现类
/**
* @author: fuheng
* 房东
*/
public class Host implements Rent{
public void rentHouse() {
System.out.println("房东出租房子");
}
}
3.代理处理程序
/**
* @author: fuheng
* 用这个类 自动生成代理类
*/
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
/**
* 生成得到代理类
* @return
*/
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
/**
* 处理代理实例 并返回结果
添加的方法在这里调用
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质 , 就是使用反射机制实现
Object result = method.invoke(rent, args);
return result;
}
}
4.测试类
/**
* @author: fuheng
* 测试类
*/
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色 : 现在没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象!
pih.setRent(host);
//这里的proxy就是动态生成的 我们并没有写
Rent proxy = (Rent) pih.getProxy();
proxy.rentHouse();
}
}
代理工具类
ProxyInvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author: fuheng
* 用这个类 自动生成代理类
*/
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
/**
* 生成得到代理类
* @return
*/
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
/**
* 处理代理实例 并返回结果
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质 , 就是使用反射机制实现
Object result = method.invoke(target, args);
return result;
}
}
测试类
/**
* @author: fuheng
* 测试类
*/
public class Cliect {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色 ,不存在
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//设置要代理的对象
pih.setTarget(host);
//动态生成代理类
Rent proxy = (Rent) pih.getProxy();
//调用方法
proxy.rentHouse();
}
}
11.AOP
11.1什么是AOP
11.2AOP在Spring中的作用
提供声明式事务:允许用户自定义切面
11.3使用Spring实现AOP
[重点] 使用AOP织入, 需要导入一个依赖包!
AspectJ Weaver
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
方式一: 使用Spring 的 API接口 {主要Spring API接口实现}
<aop:advisor >执行环绕增加
aop:pointcut 切入点
beforeLog
public class Log implements MethodBeforeAdvice {
/**
* 前置日志
* @param method 要执行的目标对象的方法
* @param args 参数
* @param target 目标对象
* @throws Throwable
*/
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
afterLog
public class AfterLog implements AfterReturningAdvice {
/**
*
* @param returnValue 返回值
*/
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"的方法,返回结果为: "+returnValue);
}
}
applicationContext.xml
<!-- 注册Bean-->
<bean id="userService" class="com.codefuheng.service.UserServiceImpl"/>
<bean id="log" class="com.codefuheng.log.Log"/>
<bean id="afterLog" class="com.codefuheng.log.AfterLog"/>
<!--方式一: 使用原生Spring API接口-->
<!--配置aop : 需要导入aop的约束-->
<aop:config>
<!--切入点: expression:表达式, execution(要执行的位置!* * * * *)-->
<aop:pointcut id="pointcut" expression="execution(* com.codefuheng.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
测试类
public class Mytest {
@Test
public void test(){
ApplicationContext Context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口:注意点
UserService userService = (UserService) Context.getBean("userService");
userService.add();
}
}
方式二: 自定义类实现AOP {主要是切面定义}
自定义类
/**
* @author: fuheng
* 自定义类
*/
public class DiyPoint {
public void after(){
System.out.println("=================之后==================");
}
public void before(){
System.out.println("==================之前==================");
}
}
applicationContext.xml
<!--切入的位置得注册-->
<bean id="userService" class="com.codefuheng.service.UserServiceImpl"/>
<!--方式二 : 自定义类-->
<bean id="diy" class="com.codefuheng.diy.DiyPoint"/>
<aop:config>
<!--自定义切面: ref 要引用的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.codefuheng.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
第三种 : 使用注解实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ClHRVzbj-1600918845409)(Spring(B站狂神说java)].assets/image-20200630184343364.png)
AnnotationPointCut类
/**
* @author: fuheng
*/
@Aspect//标注这个类是一个切面
public class AnnotationPointCut {
@Before("execution(* com.codefuheng.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("==============注解:方法执行前===================");
}
@After("execution(* com.codefuheng.service.UserServiceImpl.*(..))")
public void After(){
System.out.println("==============注解:方法执行后===================");
}
}
applicationContext.xml
<bean id="userService" class="com.codefuheng.service.UserServiceImpl"/>
<bean id="annotationPointCut" class="com.codefuheng.diy.AnnotationPointCut"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
12.整合Mybatis
步骤:
-
导入相关jar包
-
junit
-
Mybatis
-
mysql数据库
-
Spring
-
Spring-jdbc
-
aop织入
-
mybatis-Spring [最新的new]
-
lombok
-
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.7.RELEASE</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.5</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.20</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.7.RELEASE</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <scope>provided</scope> </dependency>
-
-
编写配置文件
-
测试
12.1回忆Mybatis
- 编写实体类
- 编写核心配置文件
- 编写接口
- 编写Mapper.xml
- 测试
12.2 Mybatis-Spring
方式一:
spring-dao.xml
-
编写数据源配置
<!--DataSource : 使用Spring的数据源替代Mybatis的配置 我们这里使用Spring提供的jdbc :org.springframework.jdbc.datasource 连接数据库 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/codefuheng?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="codefuheng"/> <property name="password" value="123321"/> </bean>
-
sqlSessionFactory
<!--sqlSessionFactory mybatis中其他的配置信息--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 数据库--> <property name="dataSource" ref="dataSource"/> <!-- mybatis中的xml配置文件--> <property name ="configLocation" value="classpath:mybatis-config.xml"/> <!-- mapper配置--> <property name="mapperLocations" value="classpath:com/codefuheng/mapper/*.xml"/> </bean>
-
sqlSessionTemplate
<!--SqlSessionTemplate : 就是我们使用的sqlSession--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!-- 只能使用构造器注入sqlSessionFactory , 因为他没有set方法--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean>
-
需要给接口加实现类 [ ]
public class UserMapperImpl implements UserMapper{ private SqlSessionTemplate sqlSession; public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } public List<User> getUserList() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.getUserList(); } }
-
将自己写的实现类, 注入到Spring中.(applicationContext.xml)
<!-- 方式一: 注入UserMapperImpl实现类--> <bean id="userMapper" class="com.codefuheng.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean>
-
测试使用即可 !
public class Mytest { @Test public void gettest(){ ApplicationContext Context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserMapper userMapper = Context.getBean("userMapper2", UserMapper.class); List<User> userList = userMapper.getUserList(); for (User user : userList) { System.out.println(user); } } }
方式二:
实现类继承SqlSessionDaoSupport
4.继承接口实现类UserMapperImpl2
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
public List<User> getUserList() {
return getSqlSession().getMapper(UserMapper.class).getUserList();
}
}
5.将自己写的实现类, 注入到Spring中.(applicationContext.xml)
<!-- 第二种:实现类继承SqlSessionDaoSupport-->
<bean id="userMapper2" class="com.codefuheng.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
6.测试类
public class Mytest {
@Test
public void gettest(){
ApplicationContext Context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = Context.getBean("userMapper2", UserMapper.class);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}
}
13.声明式事务
13.1回顾事务
- 把一组业务当成一个业务来做; 要么都成功 , 要么都失败!
- 事务在项目开发中, 十分的的重要 , 涉及到数据的一致性问题 , 不能马虎 !
- 确保完整性和一致性;
事务ACID原则:
- 原子性
- 一致性
- 隔离性
- 多个业务可能操作同一个资源, 防止数据损坏
- 持久性
- 事物一旦提交 , 无论系统发生什么问题 , 结果都不会再被影响 , 被持久化的写到存储器中!
13.2 Spring 中的事务管理
- 声明式事务 : AOP
- 编程式事务 : 需要再代码中 , 进行事务的管理
写在Spring-dao.xml中
1.配置声明式事务
<!--配置声明式事务-->
<bean id="transactionMapper" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2.配置事务通知:
注意点: 导入tx的时候需要导入这三个…
idea自动导入时 需要更改我画()的4个地方
xmlns:tx="http://www.springframework.org/schema/tx(1)"
http://www.springframework.org/schema/tx(2)
http://www.springframework.org/schema/tx(3)/spring-tx(4).xsd
<!-- 结合AOP实现事务的织入
配置事务通知:
-->
<tx:advice id="txAdvice" transaction-manager="transactionMapper">
<!--给哪些方法配置事务-->
<!--配置事务的传播特性: new propagation = -->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="select" read-only="true"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
3.配置事务切入
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.codefuheng.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
思考:
为什么需要事务?
- 如果不配置事务, 可能存在数据提交不一致的情况
- 如果我们不在SPRING中去配置声明事务 , 我们就需要在代码中手动配置事务!
务
13.1回顾事务
- 把一组业务当成一个业务来做; 要么都成功 , 要么都失败!
- 事务在项目开发中, 十分的的重要 , 涉及到数据的一致性问题 , 不能马虎 !
- 确保完整性和一致性;
事务ACID原则:
- 原子性
- 一致性
- 隔离性
- 多个业务可能操作同一个资源, 防止数据损坏
- 持久性
- 事物一旦提交 , 无论系统发生什么问题 , 结果都不会再被影响 , 被持久化的写到存储器中!
13.2 Spring 中的事务管理
- 声明式事务 : AOP
- 编程式事务 : 需要再代码中 , 进行事务的管理
写在Spring-dao.xml中
1.配置声明式事务
<!--配置声明式事务-->
<bean id="transactionMapper" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2.配置事务通知:
注意点: 导入tx的时候需要导入这三个…
idea自动导入时 需要更改我画()的4个地方
xmlns:tx="http://www.springframework.org/schema/tx(1)"
http://www.springframework.org/schema/tx(2)
http://www.springframework.org/schema/tx(3)/spring-tx(4).xsd
<!-- 结合AOP实现事务的织入
配置事务通知:
-->
<tx:advice id="txAdvice" transaction-manager="transactionMapper">
<!--给哪些方法配置事务-->
<!--配置事务的传播特性: new propagation = -->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="select" read-only="true"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
3.配置事务切入
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.codefuheng.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
思考:
为什么需要事务?
- 如果不配置事务, 可能存在数据提交不一致的情况
- 如果我们不在SPRING中去配置声明事务 , 我们就需要在代码中手动配置事务!
- 事务在项目的开发中十分重要 , 涉及到数据的一致性和完整性问题 , 不容马虎 !