1.三层架构的理解
-
项目开发: 视图层 (接收参数处理), 业务层 (处理业务), 持久层 (处理数据)
-
分层架构: 其实就是将 代码分类 , 按功能/职责划分为3类分别存放在web,service,dao包中
1.数据层
UserMapper接口
package com.xjggb.mapper;
public interface UserMapper {
/*
* 保存用户
* */
void save();
}
实现类
package com.xjggb.mapper.impl;
import com.xjggb.mapper.UserMapper;
public class UserMapperImpl implements UserMapper {
@Override
public void save() {
System.out.println("保存成功" );
}
}
2.业务层
接口文件
package com.xjggb.service;
public interface UserService {
void save();
}
接口实现类
package com.xjggb.service.impl;
import com.xjggb.mapper.UserMapper;
import com.xjggb.mapper.impl.UserMapperImpl;
public class UserServiceImpl implements UserMapper {
private UserMapperImpl userMapper = new UserMapperImpl();
@Override
public void save() {
userMapper.save();
}
}
3.视图层
package com.xjggb.controller;
import com.xjggb.service.impl.UserServiceImpl;
public class UserController {
private UserServiceImpl userService = new UserServiceImpl();
public void show(){
userService.save();
}
}
2常见的依赖问题
1.当实体类丢失,java文件将无法编译整个项目都报错
2.代码耦合度过高
切换实现类
package com.xjggb.mapper.impl;
import com.xjggb.mapper.UserMapper;
public class DaoUser implements UserMapper {
@Override
public void save() {
System.out.println(" w我是切换的实现类");
}
}
从代码中可以看出需要修改 private UserMapper userMapper = new DaoUser();对象
代码量十分大,成本太高
package com.xjggb.service.impl;
import com.xjggb.mapper.UserMapper;
import com.xjggb.mapper.impl.DaoUser;
import com.xjggb.mapper.impl.UserMapperImpl;
public class UserServiceImpl implements UserMapper {
private UserMapper userMapper = new DaoUser();
@Override
public void save() {
userMapper.save();
}
}
这个时候我们设计一个set注入,就会发生质变
package com.xjggb.service.impl;
import com.xjggb.mapper.UserMapper;
public class UserServiceImpl implements UserMapper {
private UserMapper userMapper ;
public void setUserMapper(UserMapper userMapper){
this.userMapper=userMapper;
}
@Override
public void save() {
userMapper.save();
}
}
之前的代码需要主动的创建对象,控制权在程序元的手中,使用set注入后程序不再主动性,变得被动接收对象我们程序员不再管对象的创建,系统的耦合性大大降低可以专注与业务,这就是IOC模型
小结
三层架构存在的问题?
- 代码耦合度过高
代码耦合度过高有哪些例子
- 实体类丢失
- 实体类切换
3.spring框架介绍
1.介绍
- Spring是分层的轻量级开源框架
- Spring核心IOC(控制反转)和AOP(面向对象)
- Spring像胶水一样可以对很多优秀的框架进行集成
- Spring框架采用分层的架构,结构清晰,根据不同的功能划分成多个模块
Data Access/Integration
- JDBC: 对各大数据库厂商进行抽象处理
- ORM: 集成orm框架支持对象关系映射处理
- OXM: 提供了对 Object/XML映射实现的抽象层
- JMS: 主要包含了一些制造和消费消息的特性
- Transactions: 支持编程和声明式事务管理
Web
- Websocket: 提供了WebSocket和SocketJS的实现
- Servlet: 利用MVC(model-view-controller)的实现分离代码
- Web: 提供了基础的面向 Web 的集成特性(如: 文件上传)
- Portlet: 提供了Portlet环境下的MVC实现
中间层
-
AOP: 提供了符合AOP要求的面向切面的编程实现
-
Aspects: 提供了与AspectJ的集成功能
-
Instrumentation: 提供了类植入(Instrumentation)的支持和类加载器的实现
-
Messaging: 用于构建基于消息的应用程序
Core Container
- Beans: Bean工厂与bean的装配
- Core: 依赖注入IoC与DI的最基本实现
- Content: IOC容器的企业服务扩展
- SpEl: 用于在运行时查询和操纵对象的表达式
Test
- 支持使用 JUnit 和 TestNG 对 Spring 组件进行测试
2. Spring优点
2.1 IOC解耦
- 可以将对象间的依赖关系交由spring管理
- 避免硬编码造成的程序间过渡耦合
2.2 支持AOP
- 可以使用切面编程思想对方法进行增强
2.3 支持声明式事务
- 可以通过配置或者注解的方式管理事务
- 不需要硬编码管理事务
2.4 方便测试
- 可以通过注解方便的测试Spring程序
2.5 方便集成
- 其内部提供了对各种优秀框架的直接支持
2.6 使用简单
- Spring对很多难用的API做了简单的封装
2.7 设计精良
- Spring的源码设计精妙、结构清晰值得学习
小结
1.Spring是什么?
- 分层的轻量级开源框架
2.Spring的优点
- 解耦
- 支持AOP
- 支持声明式事务
- 方便集成第三方框架
- 方便测试
- spring的源码设计精良结构清晰值的学习
4.IOC容器介绍
1.IOC是什么?
IOC(Inversion Of Control): 将对象的创建(控制)转交给第三方(工厂)完成, 也叫 控制反转
2.IOC的作用?
- 解耦:利用IOC工厂模式解耦创建对象的过程
- 解决代码耦合过高问题
- 存储对象:可以将创建好的对象存储起来重复使用
- 解决对象个数问题
- 管理依赖关系:可以将依赖对象注入需要的对象中
- 解决依赖关系问题
- 管理对象创建顺序:可以根据依赖关系创建对象
- 解决创建顺序问题
小结
- IOC是什么?
- 控制反转:将对象创建交给第三方完成
- IOC有什么用?
- 解耦
- 存储对象
5.IOC入门案例
1.创建项目
项目名 hello-spring
2.添加依赖
<dependencies>
<!-- 1. Spring IOC依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
3.编写一个类
package com.xjggb.entity;
public class User {
private Integer id;
private String username;
private String sex;
private String address;
public String toString() {
return "User{id = " + id + ", username = " + username + ", sex = " + sex + ", address = " + address + "}";
}
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.xjggb.entity.User" >
<property name="id" value="1"/>
<property name="username" value="123"/>
<property name="sex" value="456"/>
<property name="address" value="789"/>
</bean>
</beans>
5.测试
package com.xjggb;
import com.xjggb.config.BeanFactory;
import com.xjggb.controller.UserController;
import com.xjggb.entity.User;
import com.xjggb.mapper.impl.DaoUser;
import com.xjggb.mapper.impl.UserMapperImpl;
import com.xjggb.service.UserService;
import com.xjggb.service.impl.UserServiceImpl;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
public class Test01 {
public static void main(String[] args) throws IOException {
/*
* 创建Bean容器
* */
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
/*
* 获取对象
* */
User user = (User) context.getBean("user");
System.out.println("user = " + user);
}
}
小结
1.使用IOC容器需要添加那些依赖
<!-- 1. Spring IOC依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
2.配置文件中需要声明定义标签对象?
- 标签
如何创建IOC容器 配置文件开始加载的时候容器开始创建
-
new ClassPathXmlApplicationContext("application.xml");
3.注意 给属性注入值 如:
- 必须的给属性添加set方法
6.Spring配置
bean的标签属性
属性 | 说明 |
---|---|
id | 对象的引用名称;一定要唯一; 一次只能定义一个引用名称 |
name | 对象的引用名称; 与id区别是:name一次可以定义多个引用名称。 |
class | 类的全限定名称 |
init-method | 指定类中初始化方法的名称,在构造方法执行完毕后立即执行【了解】 |
destroy-method | 指定类中销毁方法名称,在销毁spring容器前执行【了解】 |
lazy-init | 设置为true表示在第一次使用对象的时候才创建,只对单例对象有效。 |
scope | 设置bean的作用范围, 取值: singleton:单例, 默认值; prototype:多例 request:web项目中,将对象存入request域中【了解】 session:web项目中,将对象存入session域中【了解】 globalsession:web项目中,将对象应用于集群环境,没有集群相当于session【了解】 |
1.别名配置
<!--别名,如果添加了别名,我们也可以使用别名获取到这个对象-->
<alias name="user" alias="userNew"/>
2.bean的配置
<bean id="user" class="com.xjggb.entity.User" >
<property name="id" value="1"/>
<property name="username" value="123"/>
<property name="sex" value="456"/>
<property name="address" value="789"/>
</bean>
3.import标签
这个import。一般用于团队开发使用,它可以将多个配置文件,导入合并为一个。
假设,现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的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">
//合并多个配置文件
<import resource="userApplicayion.xml"/>
<import resource="application.xml"/>
</beans>
4.bean的作用域
<!--p命名注入 可以直接注入属性property的值-->
<!--
scope: 设置对象的作用范围 (对象的使用模式)
singleton: 单例模式 (全项目只创建一次对象)
prototype: 多例模式 (每次使用都创建新的对象)
-->
<bean id="user01" class="com.xjggb.entity.User" p:username="来了老弟" scope="singleton"/>
<!--c命名空间注入 通过构造器注入-->
<bean id="user02" class="com.xjggb.entity.User" c:username="哈哈" scope="prototype"/>
5.bean的标签属性
<!-- 1. 创建对象并添加到IOC容器 -->
<!--
init-method: 指定初始化方法的名称
destroy-method: 指定销毁方法的名称: 销毁容器
lazy-init: 是否延迟创建对象: 如果设置为true表示对象在使用时创建
-->
<bean id="user" class="com.xjggb.entity.User" init-method="init" destroy-method="destoy" lazy-init="true"/>
测试
package com.xjggb.test;
public class Test04 {
public static void main(String[] args) {
/*
*创建容器
* */
ClassPathXmlApplicationContext classPath = new ClassPathXmlApplicationContext("application.xml");
/*
* 获取对象
* */
User user = classPath.getBean("user", User.class);
System.out.println("user = " + user);
/*
* 销毁对象
* */
classPath.close();
}
}
小结
1.多个配置文件合并后,我们使用总的配置文件就行了
2.给bean的属性使用property标签注入值必须给属性添加set方法
3.bean的声明周期
4.bean的创建,默认是单例
7.依赖注入
- 什么是依赖注入
- 如何在IOC创建对象后给对象赋值?
- IOC提供了这种赋值功能: 依赖注入( DI : Dependency Injection)
小结:
- 什么是依赖注入
- DI:是spring IOC提供给对象的赋值功能
1.构造器注入
配置文件
<bean id="user" class="com.xjggb.entity.User" >
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="来了老弟"/>
<constructor-arg index="2" value="男"/>
<constructor-arg index="3" value="噢噢噢噢"/>
</bean>
测试
package com.xjggb;
public class Test01 {
public static void main(String[] args) throws IOException {
/*
* 创建Bean容器
* */
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
/*
* 获取对象
* */
User user = (User) context.getBean("user");
System.out.println("user = " + user);
}
}
2.set方法注入(重点)
1.简单注入
package com.xjggb.entity;
public class User {
private Integer id;
private String username;
private String sex;
private String address;
省略get()set()方法tostring()方法
}
2.配置文件
<bean id="mapper" class="com.xjggb.entity.Mapper">
<!-- 注册name属性-->
<property name="name" value="我是行健乖乖霸"/>
</bean>
3.测试
package com.xjggb.test;
import com.xjggb.entity.Mapper;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test02 {
public static void main(String[] args) {
/*
*创建容器
**/
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Mapper mapper = context.getBean("mapper", Mapper.class);
System.out.println("mapper = " + mapper);
}
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="mapper" class="com.xjggb.entity.Mapper">
<!-- 注册name属性-->
<property name="name" value="我是行健乖乖霸"/>
<!-- 对数组进行注入 -->
<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>
<value>敲代码</value>
</list>
</property>
<!-- 注入Map数据 -->
<property name="card">
<map>
<entry key="身份证" value="123456"/>
<entry key="学号" value="1838110328"/>
</map>
</property>
<!--注入set数据-->
<property name="games" >
<set>
<value>刺激战场</value>
<value>王者荣耀</value>
<value>部落冲突</value>
</set>
</property>
</bean>
</beans>
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: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">
<!--p命名注入 可以直接注入属性property的值-->
<bean id="user01" class="com.xjggb.entity.User" p:username="来了老弟"/>
<!--c命名空间注入 通过构造器注入-->
<bean id="user02" class="com.xjggb.entity.User" c:username="哈哈"/>
测试:
public class Test03 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
User user01 = context.getBean("user01", User.class);
System.out.println("user01 = " + user01);
User user02 = context.getBean("user02", User.class);
System.out.println("user02 = " + user02);
}
}
小结
1.掌握set方法注入
2.c命名空间注入和p命名注入 需要添加约束
- xmlns:p=“http://www.springframework.org/schema/p”
- xmlns:c=“http://www.springframework.org/schema/c”
注意:
1.构造器注入需要添加有参和无参构造
2.set注入记得添加set方法
8.bean自动装配
- 自动装配是Spring满足bean依赖一种方式!
- Spring会在上下文中自动寻找,并自动给bean装配属性!
在Spring中有三种装配的方式:
- 在xml中显式的配置;
- 在java中显式配置;
- 隐式的自动装配bean【重要】
1.测试
//创建一个猫类
class Cat {
public void show(){
System.out.println("喵喵");
}
//创建一个狗类
public class Dog {
public void show(){
System.out.println("汪汪");
}
//一个人有两个动物
public class People {
private String name;
private Cat cat;
private Dog dog;
//省略getset方法
配置文件
<bean id="cat" class="com.xjggb.entity.Cat"/>
<bean id="dog" class="com.xjggb.entity.Dog"/>
<bean id="people" class="com.xjggb.entity.People">
<property name="name" value="来了老弟"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
测试
public class Test05 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
People people = context.getBean("people", People.class);
Dog dog = people.getDog();
dog.show();
Cat cat = people.getCat();
cat.show();
}
2.ByName自动装配
<!--
byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean id!
-->
<bean id="people" class="com.xjggb.entity.People" autowire="byName">
<property name="name" value="来了老弟"/>
</bean>
3.ByType自动装配
<!--
byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean!
-->
<bean id="people" class="com.xjggb.entity.People" autowire="byType">
<property name="name" value="小白莲"/>
</bean>
4.注解自动装配
- 直接在属性上使用即可 添加@Autowired
- 把类和属性都注入IOC容器
<!--开启注解支持-->
<contxt:component-scan base-package="com.xjggb"/>
@Component
public class Cat {
public void show(){
System.out.println("喵喵");
}
}
@Component
public class Dog {
public void show(){
System.out.println("汪汪");
}
}
@Component
public class People {
private String name;
@Autowired
private Cat cat;
@Autowired
private Dog dog;
}
测试
package com.xjggb.test;
import com.xjggb.entity.Cat;
import com.xjggb.entity.Dog;
import com.xjggb.entity.People;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test05 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
People people = context.getBean("people", People.class);
Cat cat = people.getCat();
cat.show();
}
}
5.依赖注入的注解
@Component
public class People {
/**
* @Autowired: 修饰属性, 方法
* 作用: 给属性注入一个容器中已经存在的对象
* <property name="username" ref="str"/>
* 特点: 可以根据属性类型+属性名称注入值
* * @Value: 修饰属性
* * 作用: 给属性注入一个固定的参数值 (包括配置中的参数)
* * <property name="sex" value="3"/>
*/
@Value("行健乖乖霸")
private String name;
@Autowired
private Cat cat;
@Autowired
private Dog dog;
6.为了在三层架构中识别不同的层对象延申了三个注解
- @Component: 修饰类
- @Repository: 修饰类, 持久层
- @Service: 修饰类, 业务层
- @Controller: 修饰类, 控制层
小结:
- @Component的作用?
- 创建对象并加入到IOC容器中
- 使用注解需要主键什么配置
- spring的注解扫描
- <contxt:component-scan base-package=“com.xjggb”/>
- 赋值(依赖注入)的注解有哪几个?
- @Value: 注入一个固定的参数值
- @Autowired: 修饰属性, 方法, 注入容器中已经存在的对象 (根据属性名称+类型注入值)
9.IOC总结
- spring是什么?
- 是一个分层的轻量级开源框架
- 什么是IOC?
- 控制反转:将对象交给第三方工厂创建完成
- IOC通过什么功能做到依赖关系管理
- 依赖注入
- 什么是DI?
- 依赖注入:Spring提供的给对象赋值的功能
- 创建对象被的注解
- @Component: 修饰类, 创建对象并添加到IOC容器中, 通用
- @Repository: 修饰类, 持久层
- @Service: 修饰类, 业务层
- @Controller: 修饰类, 表现层
- 依赖注入的注解有哪些?
- @Value: 修饰属性, 注入一个固定的值 (包括配置文件中的参数)
- @Autowired: 修饰属性, 方法, 注入一个容器中已经存在的对象
- 注解与配置关系
- 二选一即可
10.代理模式
提供代理类 控制(拦截)外部对目标对象的访问 的一种代码设计
1.静态代理模式
角色分析:
- 抽象角色(一些唱歌,拍戏):一般会使用接口或者抽象类来解决
- 真实角色(刘德华):被代理的角色
- 代理角色(经济人):代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户:访问代理对象的人!
1.创建接口
package proxy;
public interface Star {
/*
* 唱歌
* */
void sing(Integer meney);
/*
* 拍戏
* */
void pay(Integer money);
}
2.创建目标类(刘德华)
package proxy;
public class LiuStar implements Star {
@Override
public void sing(Integer meney) {
System.out.println("刘德华唱歌真好听, 收费: "+meney);
}
@Override
public void pay(Integer money) {
System.out.println("刘德华拍戏棒棒哒, 收费: "+money);
}
}
3.创建代理类(经济人)
package proxy;
public class LiuStarProxy implements Star {
private Star star ;
public void setLiuStar(Star star){
this.star=star;
}
@Override
public void sing(Integer meney) {
show();
star.sing(meney);
}
@Override
public void pay(Integer money) {
//对方法进行加强
show();
test01();
star.pay(money);
}
public void test01(){
System.out.println("中间添加l电影收费");
System.out.println("中间添加了代言提成");
}
public void show(){
System.out.println("对方法进行加强了");
}
}
4.测试
package com.xjggb.test;
import proxy.LiuStar;
import proxy.LiuStarProxy;
import proxy.Star;
public class Test06 {
public static void main(String[] args) {
/*
* 刘德华懂唱歌
*拍戏
* */
LiuStar liuStar = new LiuStar();
/*
* 代理类 也就是经济人
* */
LiuStarProxy liuStarProxy = new LiuStarProxy();
liuStarProxy.setLiuStar(liuStar);
/*
* 不需要直接找到刘德华说谈拍戏或者唱歌
* 只需要找经济人即可
* 然后经济人可以从中做手脚,
* */
liuStarProxy.pay(123);
liuStarProxy.sing(123);
}
}
小结:
1.代理模式的好处
- 可以使刘德华更专注的去唱歌,拍戏,不需要去关注其他的业务
- 公共的角色交给代理角色,实现了业务的分工
- 公共的业务发生扩展,方便管理
2.缺点:
- 一个真正的角色就会产生一个代理角色,代码量太大,影响开发效率
2.动态代理
动态代理和静态代理角色一样
动态代理的代理类是动态生成的,不是我们直接写好的!
动态代理分为两大类:基于接口的动态代理,基于类的动态代理
1.接口
package proxy;
public interface Star {
/*
* 唱歌
* */
void sing(Integer meney);
/*
* 拍戏
* */
void pay(Integer money);
}
2.创建目标类(刘德华)
package proxy;
public class LiuStar implements Star {
@Override
public void sing(Integer meney) {
System.out.println("刘德华唱歌真好听, 收费: "+meney);
}
@Override
public void pay(Integer money) {
System.out.println("刘德华拍戏棒棒哒, 收费: "+money);
}
}
3.动态代理类
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
private Object star;
public void setObject(Object star){
this.star=star;
}
public Object getProxy(){
return Proxy.newProxyInstance(
this.getClass().getClassLoader(),
star.getClass().getInterfaces(),
this
);
}
/**
* 当代理方法被调用会触发该方法
* @param proxy 代理对象
* @param method 方法
* @param args 参数
* @return 方法返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
* 获取参数
* */
show01();
Integer arg = (Integer) args[0];
if(arg>123){
//调用invoke方法
Object invoke = method.invoke(star, args);
return invoke;
}else {
System.out.println("经费不够");
}
return null;
}
//增强方法
public void show01(){
System.out.println("我是经济人吞了一半的经费");
}
}
4.测试
package com.xjggb.test;
import proxy.LiuStar;
import proxy.ProxyInvocationHandler;
import proxy.Star;
public class Test08 {
public static void main(String[] args) {
/*
* 真实对象
* */
LiuStar liuStar = new LiuStar();
/*
* 代理类
* */
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
proxyInvocationHandler.setObject(liuStar);
/*
* 获取代理对象
* */
Star proxy = (Star) proxyInvocationHandler.getProxy();
proxy.pay(123);
}
}
cglib动态代理的案例
官方API
public static Object create(
// 1. 代理对象需要继承的类
java.lang.Class superclass,
// 2. 代理对象需要实现的接口
java.lang.Class[] interfaces,
// 3. 代理对象方法调用时的处理器 InvocationHandler
Callback[] callbacks
)
代理类
/**
* 演示: Cglib动态代理
* 1. 目标对象可以不实现接口
*/
@Test
public void testCglib(){
LiuStar liuStar = new LiuStar();
// 1. 创建代理对象
Star star = (Star) Enhancer.create(
// 1. 需要继承的父类字节码
LiuStar.class,
// 2. 接口字节码数组 (非必须传递接口)
new Class[]{ /*Star.class*/ },
// 3. 方法调用处理器
new org.springframework.cglib.proxy.InvocationHandler() {
/**
* 当代理方法被调用会触发该方法
* @param proxy 代理对象
* @param method 方法
* @param args 参数
* @return 方法返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Integer money = (Integer) args[0];
if(money > 10000){
return method.invoke(liuStar, args);
}else {
System.out.println("档期忙!!!");
}
return null;
}
}
);
System.out.println("==========================="+star.getClass());
// 2. 调用代理方法
star.sing(100);
star.pay(10001);
}
小结
- 常见的动态代理有哪些?
- JDK,CGLIB
- l两种代理的的区别
- jdk:必须要实现接口
- cglib:可以不需要接口
11.AOP
1.什么是AOP?
(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
2. 相关的术语
2.1 连接点
- 目标对象中的所有方法
2.2 切入点
- 目标对象中需要增强的方法
2.3 通知
- 目标对象需要增强的内容 (和位置)
2.4 切面
- 切面是由切入点 和 通知组成的关系
小结
- Spring AOP 的原理是什么?
- 动态代理技术
- 请描述下列术语的含义:
- 连接点: 目标对象中所有的方法
- 切入点: 目标对象中需要增强的方法
- 通知: 目标方法需要增强的内容 (代码)
- 切面: 是切入点和通知组成的关系
3.AOP入门案例
1.方式1
1添加依赖
<?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>hello-spring</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- 1. Spring IOC 和 AOP依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 2. Aspectj 依赖 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<!-- 3. Spring Test 依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
2.需要加强的类
public class UserService {
@Override
public void save() {
System.out.println("保存成功");
}
}
3.增强类
public class LogAdvice {
/**
* 日志工具类 (通知类/增强类/切面类).
*
* @author : Jason.lee
* @version : 1.0
*/
/**
* 通知方法/增强方法
*/
public void log(){
System.out.println("日志已经打印...");
}
}
4.配置文件
<!--创建对象放进容器中-->
<bean id="service" class="com.xjggb.service.impl.UserServiceImpl"/>
<bean id="logAdvice" class="com.xjggb.log.LogAdvice"/>
<!--配置AOP-->
<aop:config>
<!-- 1. 定位切入点 -->
<!--
<aop:pointcut: 定义切入点
id: 切入点名称
expression: 表达式的内容
-->
<aop:pointcut id="pt" expression="execution(* com.xjggb.service.impl.UserServiceImpl.*(..))"/>
<!-- 2. 定义切面 -->
<!--
<aop:aspect: 定义切面
ref: 引用一个通知类
-->
<aop:aspect ref="logAdvice">
<!-- 主要配置通知方法和切入点的关系 -->
<!--
<aop:after: 定义一个后置(最终)通知
method: 通知方法名称
pointcut-ref: 切入点的引用
-->
<aop:after method="log" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
5.测试
public class Test09 {
public static void main(String[] args) {
ClassPathXmlApplicationContext classContext = new ClassPathXmlApplicationContext("application.xml");
UserServiceImpl service = classContext.getBean("service", UserServiceImpl.class);
service.save();
}
}
6.固定使用Cglib
-
applicationContext.xml
<!-- <aop:config: 配置AOP proxy-target-class: 配置是否强制使用cglib动态代理 --> <aop:config proxy-target-class="true">
小结
-
自动记录日志的原理是什么?
- AOP面向切编程
-
两种动态代理Spring是如何选择的?
- 默认. 如果没有实现接口使用CGLIB, 如果实现了接口则使用JDK (可以固定Cglib)
2.方式二
1.编写接口
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
2.编写实现类
4.切入点表达式(了解)
1. 什么是切入点表达式
- 定位切入点的1种语法规则
2. 切入点表达式指示符
- 切入点表达式常见的 指示符 (PCD)有以下三种
指示符 | 示例 | 作用(细粒度) |
---|---|---|
bean | bean(accountService) | 精确到IOC容器的bean |
within | within(com.itheima.xml.service.impl.AccountServiceImpl) | 精确到类 |
execution | execution(public void com.itheima…save(…)) | 精确到方法 |
<!-- 1. 创建对象, 放到容器 -->
<bean id="service" class="com.xjggb.service.impl.UserServiceImpl"/>
<bean id="logAdvice" class="com.xjggb.log.LogAdvice"/>
<!-- 2. 配置AOP -->
<!--
<aop:config: 配置AOP
proxy-target-class: 是否固定使用Cglib代理生成代理类
-->
<aop:config proxy-target-class="true">
<!-- 1. 定位切入点 -->
<!--
<aop:pointcut: 定义切入点
id: 切入点名称
expression: 表达式的内容
-->
<!--
bean: 定位容器中的对象名称: 对象中所有方法都是切入点
value: 对象名称
*: 匹配任何字符串
-->
<!-- <aop:pointcut id="pt" expression="bean(accountService)"/>-->
<aop:pointcut id="pt" expression="bean(*Service)"/>
<!-- 2. 定义切面 -->
<!--
<aop:aspect: 定义切面
ref: 引用一个通知类
-->
<aop:aspect ref="logAdvice">
<!-- 主要配置通知方法和切入点的关系 -->
<!--
<aop:after: 定义一个后置(最终)通知
method: 通知方法名称
pointcut-ref: 切入点的引用
-->
<aop:after method="log" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
<!-- 1. 定位切入点 -->
<!--
<aop:pointcut: 定义切入点
id: 切入点名称
expression: 表达式的内容
-->
<!--
bean: 定位容器中的对象名称: 对象中所有方法都是切入点
value: 对象名称
*: 匹配任何字符串
-->
<!-- <aop:pointcut id="pt" expression="bean(accountService)"/>-->
<!-- <aop:pointcut id="pt" expression="bean(*Service)"/>-->
<!--
within: 定位项目中的类
value: 全限定类名
*: 匹配任何字符串 (路径中只能代表1层)
..: 匹配任意个路径
-->
<!-- <aop:pointcut id="pt" expression="within(com.itheima.xml.impl.AccountServiceImpl)"/>-->
<!-- <aop:pointcut id="pt" expression="within(com.itheima.xml.impl.*ServiceImpl)"/>-->
<!-- <aop:pointcut id="pt" expression="within(com.itheima.xml.impl.*Service*)"/>-->
<!-- <aop:pointcut id="pt" expression="within(com.itheima.xml.*.*Service*)"/>-->
<!-- <aop:pointcut id="pt" expression="within(com.itheima.*.*.*Service*)"/>-->
<!-- <aop:pointcut id="pt" expression="within(*..*)"/>-->
<!--
execution: 精确到方法
*: 匹配任何字符串 (路径中只能代表1层) (在参数列表中表示1个参数)
..: 匹配任意个路径 (在参数列表中表示任意个参数)
-->
<!-- <aop:pointcut id="pt" expression="execution(public void com.itheima.xml.impl.AccountServiceImpl.trans())"/>-->
<!-- <aop:pointcut id="pt" expression="execution(public void com.itheima.xml.*.AccountServiceImpl.trans())"/>-->
<!-- <aop:pointcut id="pt" expression="execution(public void com.itheima.*.*.AccountService*.trans())"/>-->
<!-- <aop:pointcut id="pt" expression="execution(public void *..*.AccountService*.trans())"/>-->
<!-- <aop:pointcut id="pt" expression="execution(public void *..*.AccountService*.*())"/>-->
<!-- <aop:pointcut id="pt" expression="execution(void *..*.AccountService*.*())"/>-->
<!-- <aop:pointcut id="pt" expression="execution(* *..*.AccountService*.*())"/>-->
<!-- <aop:pointcut id="pt" expression="execution(* *..*.AccountService*.*(..))"/>-->
<!-- <aop:pointcut id="pt" expression="execution(* *..AccountService*.*(..))"/>-->
<!-- <aop:pointcut id="pt" expression="execution(* *..*.*(..))"/>-->
<!-- <aop:pointcut id="pt" expression="execution(* *.*(..))"/>-->
<!-- 省略了public, 省略了全限定类名 -->
<aop:pointcut id="pt" expression="execution(* *(..))"/>
<!-- 2. 定义切面 -->
<!--
<aop:aspect: 定义切面
ref: 引用一个通知类
-->
<aop:aspect ref="logAdvice">
<!-- 主要配置通知方法和切入点的关系 -->
<!--
<aop:after: 定义一个后置(最终)通知
method: 通知方法名称
pointcut-ref: 切入点的引用
-->
<aop:after method="log" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
小结
- 什么是切入点表达式?
- 定位切入点的1中语法规则
- 常用的指示符有什么区别?
- bean: 精确到容器中的对象中所有方法
- within: 精确到类中所有的方法
- execution: 精确到方法
5.AOP通知类型(了解)
*/
public class LogAdvice {
/**
* 通知方法/增强方法
*/
public void log(){
System.out.println("日志已经打印...");
}
public void before(){
System.out.println("前置通知..");
}
public void afterReturning(){
System.out.println("后置通知..");
}
public void afterThrowing(){
System.out.println("异常通知..");
}
public void after(){
System.out.println("异常通知..");
}
}
<!--创建对象放进容器中-->
<bean id="service" class="com.xjggb.service.impl.UserServiceImpl"/>
<bean id="logAdvice" class="com.xjggb.log.LogAdvice"/>
<!--配置AOP-->
<aop:config>
<!-- 1. 定位切入点 -->
<!--
<aop:pointcut: 定义切入点
id: 切入点名称
expression: 表达式的内容
-->
<aop:pointcut id="pt" expression="execution(* com.xjggb.service.impl.UserServiceImpl.*(..))"/>
<!-- 2. 定义切面 -->
<!--
<aop:aspect: 定义切面
ref: 引用一个通知类
-->
<aop:aspect ref="logAdvice">
<!-- 主要配置通知方法和切入点的关系 -->
<!--
<aop:after: 定义一个后置(最终)通知
method: 通知方法名称
pointcut-ref: 切入点的引用
-->
<!--前置通知-->
<aop:after method="before" pointcut-ref="pt"/>
<!--后置通知-->
<aop:after-returning method="afterReturning" pointcut-ref="pt"/>
<!--异常通知-->
<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
<!--最终通知-->
<aop:after method="log" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
小结:
- 通知分几类
- 前置
- 后置
- 异常
- 最终通知
6.环绕通知(重要)
1.方式一
1.需要增强的类
public class UserServiceImpl {
public void add() {
System.out.println("增加了一个用户!");
}
public void delete() {
System.out.println("删除了一个用户!");
}
public void update() {
System.out.println("更新了一个用户!");
}
public void select() {
System.out.println("查询了一个用户!");
}
}
2.增强的方法
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
}
}
public class Log implements MethodBeforeAdvice {
//method: 要执行的目标对象的方法
//args:参数
//target:目标对象
@Override
public void before(Method method, Object[] objects, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
3.配置文件
<!--创建对象放进容器中-->
<bean id="userService" class="com.xjggb.logservice.impl.UserServiceImpl"/>
<bean id="log" class="com.xjggb.logservice.log.Log"/>
<bean id="logAdvice" class="com.xjggb.logservice.log.AfterLog"/>
<aop:config>
<aop:pointcut id="pt" expression="execution(* com.xjggb.logservice.impl.*.*(..))"/>
<!--开启环绕通知-->
<aop:advisor advice-ref="logAdvice" pointcut-ref="pt"/>
<aop:advisor advice-ref="log" pointcut-ref="pt"/>
</aop:config>
4.测试
public class Test10 {
public static void main(String[] args) {
ClassPathXmlApplicationContext classContext = new ClassPathXmlApplicationContext("application.xml");
UserServiceImpl service = classContext.getBean("userService", UserServiceImpl.class);
service.add();
service.delete();
service.select();
service.update();
}
}
小结:
- 增强类需要实现AfterReturningAdvice接口,有返回值
- 增强类需要实现MethodBeforeAdvice接口 没有返回值的
2.方式二
注解配置
1.定义一个增强类
package com.xjggb.logservice.log;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component //将类注入容器交给Spring管理
@Aspect //标志这个类是一个切面类
public class SectionClass {
/**
* 通知方法/增强方法
*/
@Pointcut("execution(* com.xjggb.logservice.impl.UserServiceImpl.*(..))")
public void log(){
System.out.println("日志已经打印...");
}
//方法执行前通知
@Before("log()")
public void before(){
System.out.println("前置通知");
}
@AfterReturning("log()")
public void show01(){
System.out.println("后置通知");
}
//异常通知
@AfterThrowing("log()")
public void AfterThrowing(){
System.out.println("异常通知");
}
//最终通知
@After("execution(* com.xjggb.logservice.impl.UserServiceImpl.*(..))")
public void show(){
System.out.println("最终通知");
}
@Around("log()") //添加切入点
public void show(ProceedingJoinPoint joinPoint){
System.out.println(" 环绕前" );
Signature signature = joinPoint.getSignature();
System.out.println("signature = " + signature);
try {
before();
Object proceed = joinPoint.proceed();
show();
} catch (Throwable throwable) {
throwable.printStackTrace();
AfterThrowing();
}finally {
show();
}
System.out.println();
}
}
2.需要被增强的类
public class UserServiceImpl {
public void add() {
System.out.println("增加了一个用户!");
}
public void delete() {
System.out.println("删除了一个用户!");
}
public void update() {
System.out.println("更新了一个用户!");
}
public void select() {
System.out.println("查询了一个用户!");
}
}
3.配置文件
<!-- 进行包扫描-->
<context:component-scan base-package="com.xjggb.logservice.log"/>
<!-- 添加注解支持 -->
<aop:aspectj-autoproxy/>
<bean id="userService" class="com.xjggb.logservice.impl.UserServiceImpl"/>
4.测试
public class Test10 {
public static void main(String[] args) {
ClassPathXmlApplicationContext classContext = new ClassPathXmlApplicationContext("application.xml");
UserServiceImpl service = classContext.getBean("userService", UserServiceImpl.class);
service.add();
service.delete();
service.select();
service.update();
}
}
小结
1.请理解以下注解的作用?
- @Aspect: 修饰类, 修饰为切面类
- @Pointcut: 修饰方法, 定义切入点表达式
- @Around: 修饰方法, 修饰为环绕通知方法
- @Before 修饰方法 修饰为前置通知
- @AfterReturning 修饰方法 修饰为方法执行后通知
- @AfterThrowing 修饰方法 修饰方法执行后发生异常通知
- @After 修饰方法 表示最终通知
2.ProceedingJoinPoint 对象的使用?
-
获取切入点所在目标对象
- joinPoint.getTarget();
- joinPoint.getTarget().getClass().getName(); 获取类名
-
获取切入点方法的名字
- joinPoint.getSignature().getName()
-
获取方法上的注解
- 方法1:xxxxxx是注解名字
Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); if (method != null) { //xxxxxx 注解的名字 xxxxxx annoObj= method.getAnnotation(xxxxxx.class); } return null;
2.方法2:上面我们已经知道了方法名和类的对象,通过反射可以获取类的内部任何信息。
// 切面所在类 Object target = joinPoint.getTarget(); String methodName = joinPoint.getSignature().getName(); Method method = null; for (Method m : target.getClass().getMethods()) { if (m.getName().equals(methodName)) { method = m; // xxxxxx annoObj= method.getAnnotation(xxxxxx.class);同上 break; } }
-
获取方法的参数
- Object[] args = joinPoint.getArgs(); //返回切入点的的方法参数列表
12.AOP总结
- 什么是代理模式?
- 提供代理类防止外部目标直接对目标对象进行访问
- 动态代理JDK和CGLIB的区别?
- jdk:目标对象需要实现接口
- cglib:目标对象可以不实现接口
- SpringAOP的底层技术是什么?
- 动态代理技术(代理模式)
- 切面和和切入点以及通知是什么?
- 切入点:目标对象需要增强的方法
- 通知:目标方法需要增强的内容
- 切入点通知的关系
- 通知类型有哪些
- 前置通知
- 后置通知
- 异常通知
- 最终通知、
- AOP的相关注解有哪些
- @Aspect: 修饰类, 修饰为切面类
- @Pointcut: 修饰方法, 定义切入点表达式
- @Around: 修饰方法, 修饰为环绕通知方法
- @Before 修饰方法 修饰为前置通知
- @AfterReturning 修饰方法 修饰为方法执行后通知
- @AfterThrowing 修饰方法 修饰方法执行后发生异常通知
- @After 修饰方法 表示最终通知
12.Spring编程式事务
-
事务的作用是什么?
- 在业务中保证数据安全
-
什么是ACID原则?
-
Atomicity: 原子性: 不可拆分事务中的多个事件: 要么同时成功, 要么同时失败
-
Consistency: 一致性: 事务执行前后的数据总量保持不变
-
Isolation: 隔离性: 多个事务之间相对独立, 互不影响
-
Durability: 持久性: 事务执行之后的结果会永久保存起来
-
-
以下隔离级别分别解决了什么问题?
-
ISOLATION_READ_UNCOMMITTED: 没有解决任何问题 (有脏读问题)
-
ISOLATION_READ_COMMITTED: 解决了脏读问题
-
ISOLATION_REPEATABLE_READ: 解决了不可重复读问题
-
ISOLATION_SERIALIZABLE: 解决了幻读问题
-
-
请说出以下传播行为的含义:
-
REQUIRED: 需要
-
SUPPORTS: 支持
-
MANDATORY: 强制
-
REQUIRES_NEW: 新
-
NOT_SUPPORTED: 不支持
-
NEVER: 决不
-
NESTED: 嵌套
-
-
声明式事务的好处是什么?
- 只需要一个小小的声明即可获得事务管理功能
-
请描述以下注解的作用?
-
@EnableTransactionManagement: 修饰类, 开启事务管理功能 (注解支持)
-
@Transactional: 修饰类, 方法: 给方法添加事务管理功能
-
-
Spring的优点有哪些?
- 解耦
- 支持AOP
- 支持声明式事务