视频
Tomcat Javaweb服务器 帮助我们去集中去处理请求和响应 包括完成Servlet以及相关代码的运行和解析
而其中Servlet引擎 最核心的功能
Controller | 控制器 |
---|---|
Service | 业务处理 |
DAO | 数据库的访问和操作 |
DB | 数据库 |
整合设计模式
设计模式
⾯向对象设计中,解决特定问题的经典代码
GOF4⼈帮定义的23种设计模式:⼯⼚、适配器、装饰器、⻔⾯、代理、模板… GOF4 (面向对象领域的4个大师)
1.直接调用构造方法创建对象 UserService userService=new UserServiceImpl();
2.通过反射的形式创建对象解耦合
Class clazz=Class.forName("com.baizhiedu.basic.UserServiceImpl");//返回一个类 目的:动态加载类
//缺点:实现类硬编码在程序中 解决耦合:工厂类创建对象
UserService userService=(UserService)clazz.newInstance(); //根据类中的ewInstance()方法获取UserService对象
//返回的是Object而此时需要获取的是UserService所以这里要强制类型转换
Class.forName("")返回的是类
Class.forName("").newInstance()返回的是object
方法声明5个要素 修饰符、返回值类型、方法名、参数表、异常(可选)
public class BeanFactory {
private static Properties env = new Properties();
//IO 系统级资源 尽量避免重复打开IO 而且最好是在程序启动的时候一次性读取想要的内容 用静态代码块的方式来完成
static{
try {
//为避免耦合 类名最好写在配置文件中
//第一步 获得IO输入流
InputStream inputStream=BeanFactory.class.getResourceAsStream("/applicationContext.properties");
//第二步 文件内容到封装 Properties集合中 key = userService value = com.baizhixx.UserServiceImpl
env.load(inputStream);
inputStream.close(); //关闭IO输入流
} catch (IOException e) {
e.printStackTrace();
}
}
/* 这里结构所使用的代码基本一致 只有小配置文件的key是有所区别的
* 所以为每一个对象提供一个独立的工厂是没有价值和意义的
*/
public static Object getBean(String key){
Object ret = null;
try {
Class clazz = Class.forName(env.getProperty(key));
ret = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return ret;
}
/* test文件中 */
@Test
public void test1() {
UserService userService = (UserService)BeanFactory.getBean("userService");
//返回的是Object但实际需要的是UserService 所以需要强制类型转换
userService.login("name", "suns"); //具体userService对象的使用
User user = new User("suns", "123456");
userService.register(user);
}
Java Properties类:用于读取java的配置文件
https://www.cnblogs.com/bakari/p/3562244.html
load ( InputStream inStream),从输入流中读取属性列表(键和元素对)。通过对指定的文件(比如说xxx.properties 文件)进行装载来获取该文件中的所有键 - 值对。以供 getProperty ( String key) 来搜索。
Class clazz=Class.forName(env.getProperty("userService")); //根据这个值获得UserServiceImpl这个类
//等价于 Class clazz=Class.forName("com.baizhiedu.basic.UserServiceImpl");
userService=(UserService)clazz.newInstance();
工厂核心目的:创建对象(而创建对象首先需要先定义类型)
通过new创建对象是有耦合的 所以我们需要通过工厂来创建对象
让工厂类认识我们新定义的类:告诉工厂我们要创建什么类的对象
通过配置文件的配置来告知工厂
通过⼯⼚获得类的对象 Object ret =BeanFactory.getBean("key")
//通过这种⽅式获得对象,就不需要强制类型转换
Person person =ctx.getBean("person",Person.class);
//当前Spring的配置⽂件中 只能有⼀个bean class是Person类型
Person person =ctx.getBean(Person.class);
//获取的是 Spring工厂配置文件中所有bean标签的id值 person person1
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("beanDefinitionName = " + beanDefinitionName);
}
//根据类型获得Spring配置文件中对应的id值
String[] beanNamesForType = ctx.getBeanNamesForType(Person.class);
for (String id : beanNamesForType) {
System.out.println("id = " + id);
}
//用于判断是否存在指定id值得bean,不能判断name值
if (ctx.containsBeanDefinition("person")) {
System.out.println("true = " + true);
}else{
System.out.println("false = " + false);
}
//用于判断是否存在指定id值得bean,也可以判断name值
if (ctx.containsBean("p")) {
System.out.println("true = " + true);
}else{
System.out.println("false = " + false);
}
第一步 读配置文件获取class信息
第二步 通过反射来创建对应的对象 通过getBean方法交给我们来进行后续的使用
第三步 反射创建对象 底层也是会调用对象自己的构造方法 所以反射创建对象等效于new创建对象
通过Spring⼯⼚及配置⽂件,为所创建对象的成员变量赋值
为什么需要注⼊?
通过编码的⽅式,为成员变量进⾏赋值,存在耦合
好处:解耦合
如何进⾏注⼊?
private Integer id;
private String name;
//Getter and Setter
public Integer getId() {return id;}
public void setId(Integer id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
<bean id="person" name="p" class="com.baizhiedu.basic.Person">
<property name="id">
<value>20value>
property>
<property name="name">
<value>zyvalue>
property>
bean>
基于属性简化
<bean id="person" name="p" class="com.baizhiedu.basic.Person">
<property name="id" value="20"/>
<property name="name" value="zy"/>
bean>
基于p命名空间的简化
在
P:
中间按alt+enter 选择Create namespace declaration
<bean id="person" name="p" class="com.baizhiedu.basic.Person" p:id="20" p:name="zy"/>
<property name="addresses">
<list>
<value>zparkvalue>
<value>shangdivalue>
<value>xierqvalue>
<value>xierqvalue>
<value>xierqvalue>
list>
property>
<property name="tels">
<set>
<value>138111111value>
<value>139111111value>
<value>166111111value>
<value>166111111value>
<value>166111111value>
set>
property>
<property name="addresses">
<list>
<value>zparkvalue>
<value>shangdivalue>
<value>xierqvalue>
<value>xierqvalue>
<value>xierqvalue>
list>
property>
Map集合
<property name="qqs">
<map>
<entry>
<key><value>sunsvalue>key>
<value>3434334343value>
entry>
<entry>
<key><value>chenynvalue>key>
<value>73737373value>
entry>
map>
property>
Map<String, String> qqs = person.getQqs();
Set<String> keys = qqs.keySet(); //通过keySet获得对象里的key 然后通过对象.get(key)获得value
for (String key : keys) {
System.out.println(key + " value is " + qqs.get(key));
}
Properites
<props>
<prop key="key1">
value1
prop>
<prop key="key2">
value2
prop>
props>
public class UserServiceImpl implements UserService {
// private UserDAO userDAO = new UserDAOImpl();
// private UserDAO userDAO = (UserDAO)BeanFactory.getBean("userDAO");
private UserDAO userDAO; //具体的赋值使用配置文件来完成->解耦合
public UserDAO getUserDAO() {
return userDAO;
}
public void setUserDAO(UserDAO userDAO) { //通过set方法为UserDAO对象赋值
this.userDAO = userDAO;
}
@Override
public void register(User user) {
userDAO.save(user);
}
@Override
public void login(String name, String password) {
userDAO.queryUserByNameAndPassword(name, password);
}
}
userService接口里的UserServiceImpl想要使用UserDAO接口里的UserDAOImpl类的方法
<bean id="userService" class="com.baizhiedu.basic.UserServiceImpl">
<property name="userDAO">
<bean class="com.baizhiedu.basic.UserDAOImpl"/>
</property>
</bean>
@Test
public void test10() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.register(new User("suns", "123456")); //UserDAO里面的方法可以使用
userService.login("xiaohei", "999999");
}
第⼀种赋值⽅式存在的问题:
<bean id="userDAO" class="com.baizhiedu.dao.UserDAOImpl">bean>
<bean id="userService" class="com.baizhiedu.service.UserServiceImpl">
<property name="userDAO">
<ref bean="userDAO"/>
property>
bean>
基于属性简化
<bean id="userDAO" class="com.baizhiedu.dao.UserDAOImpl">bean>
<bean id="userService" class="com.baizhiedu.service.UserServiceImpl">
<property name="userDAO" ref="userDAO"/>
bean>
基于p命名空间的简化
<bean id="userDAO" class="com.baizhiedu.dao.UserDAOImpl">bean>
<bean id="userService" class="com.baizhiedu.service.UserServiceImpl" p:userDAO-ref="userDAO"/>
public class Customer {
private String name;
private int age;
public Customer(String name) {this.name = name;}
public Customer(int age) {this.age = age;}
public Customer(String name, int age) { //被重载的方法必须改变参数列表(参数个数或类型或顺序不一样);
this.name = name;
this.age = age;
}
}
<bean id="customer" class="com.baizhiedu.basic.constructer.Customer">
<constructor-arg type="int">
<value>20value>
constructor-arg>
bean>
反转控制(IOC Inverse of Control)
控制:对于成员变量赋值的控制权;
反转控制:把对于成员变量赋值的控制权,从代码中转移(反转)到 Spring ⼯⼚和配置⽂件中完成。
好处:解耦合;
底层实现:工厂设计模式;
依赖注入 (Dependency Injection - DI)
注⼊:通过 Spring 的⼯⼚及配置⽂件,为对象(bean,组件)的成员变量赋值;
依赖注⼊:当⼀个类需要另⼀个类时,就意味着依赖,⼀旦出现依赖,就可以把另⼀个类作为本类的成员变量,最终通过 Spring 配置⽂件进⾏注⼊(赋值)
好处:解耦合;
public class ConnectionFactoryBean implements FactoryBean<Connection> {
// 用于书写创建复杂对象时的代码
@Override
public Connection getObject() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/spring", "root", "1234");
return conn;
}
// 返回创建的复杂对象的类型
@Override
public Class<Connection> getObjectType() {
return Connection.class;
}
// 是否单例
@Override
public boolean isSingleton() {
return false; // 每一次都创建新的复杂对象
// return true; // 只创建一次这种类型的复杂对象
}
}
<bean id="conn" class="com.baizhiedu.factorybean.ConnectionFactoryBean"/>
如果就想获得
FactoryBean
类型的对象,加个&
,ctx.getBean("&conn")
ConnectionFactoryBean cfb = (ConnectionFactoryBean) ctx.getBean("&conn");
Spring 内部运行流程:
public class ConnectionFactory {
public Connection getConnection() {
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/spring?useSSL=false", "root", "1234");
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return conn;
}
}
<bean id="connFactory" class="com.baizhiedu.factorybean.ConnectionFactory"/>
<bean id="conn" factory-bean="connFactory" factory-method="getConnection"/>
<bean id="conn" class="com.baizhiedu.factorybean.StaticFactoryBean" factory-method="getConnection"/>
配置文件中进行配置:
sigleton:只会创建⼀次简单对象,默认值;
prototype:每⼀次都会创建新的对象;
<bean id="scope" scope="singleton" class="com.baizhiedu.scope.Scope"/>
如果是 FactoryBean 方式创建的复杂对象:
public class xxxFactoryBean implements FactoryBean {
public boolean isSingleton() {
return true; // 只会创建⼀次
// return false; // 每⼀次都会创建新的
}
}
Spring 工厂何时创建对象?
scope="prototype"
:Spring 工厂在获取对象 ctx.getBean("xxx")
的同时,创建对象。scope="singleton"
:Spring 工厂创建的同时,创建对象。
也可以实现工厂获取对象的同时,创建对象。什么时候?Spring 工厂在创建完对象后,调用对象的初始化方法,完成对应的初始化操作。
初始化方法提供:程序员根据需求,提供初始化方法,最终完成初始化操作。
初始化方法调用:Spring 工厂进行调用。
提供初始化方法的两种方式:
InitializingBean
接口:
public cass Product implements InitializingBean {
//程序员根据需求实现的方法, 完成初始化操作
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Product.afterPropertiesSet");
}
}
对象中提供一个普通的初始化方法,配置文件种配置 init-method
:
public class Product {
public void myInit() {
System.out.println("Product.myInit");
}
}
<bean id="product" class="com.baizhiedu.life.Product" init-method="myInit"/>
InitializingBean
同时⼜提供的 普通的初始化方法,执行顺序?InitializingBean
,再执行 普通初始化方法。Spring 销毁对象前,会调用对象的销毁方法,完成销毁操作。
Spring 什么时候销毁所创建的对象?ctx.close();
销毁方法提供:程序员根据业务需求,定义销毁方法,完成销毁操作
销毁方法调用:Spring 工厂进行调用。
开发流程与初始化操作一样,提供销毁方法的两种方式:
DisposableBean 接口:
public class Product implements DisposableBean {
// 程序员根据⾃⼰的需求, 定义销毁方法, 完成销毁操作
@Override
public void destroy() throws Exception {
System.out.println("Product.destroy");
}
}
对象中提供一个普通的销毁方法,配置文件种配置 destroy-method
:
public class Product {
// 程序员根据⾃⼰的需求, 定义销毁方法, 完成销毁操作
public void myDestory() {
System.out.println("Product.myDestory");
}
}
<bean id="product" class="com.baizhiedu.life.Product" destroy-method="myDestory"/>
scope="singleton"
,初始化操作都适用。io.close()
、connection.close()
、…${key}
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/spring?useSSL=false
jdbc.username = root
jdbc.password = 1234
<context:property-placeholder location="classpath:/db.properties"/>
<bean id="conn" class="com.baizhiedu.factorybean.ConnectionFactoryBean">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
bean>
public class MyDateConverter implements Converter<String, Date> { //<原始类型,转换好的类型>
/*
convert方法作用: String ---> Date
SimpleDateFormat sdf = new SimpleDateFormat();
sdf.parset(String) ---> Date
参数:
source : 代表的是配置文件中, 日期字符串 2020-10-11
return : 当把转换好的 Date 作为 convert 方法的返回值后,
Spring ⾃动的为birthday属性进行注入(赋值)
*/
@Override
public Date convert(String source) { //声明的是Data类型的转换器
Date date = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
date = sdf.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
<bean id="myDateConverter" class="com.baizhiedu.converter.MyDateConverter"/>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="myDateConverter"/>
set>
property>
bean>
<bean id="good" class="com.baizhiedu.converter.Good">
<property name="name" value="zy"/>
<property name="birthday" value="2020-6-12"/>
bean>
细节处理
MyDateConverter
中的日期的格式,通过 依赖注入 的方式,由配置文件完成赋值。
public class MyDateConverter implements Converter<String, Date> {
private String pattern;
@Override
public Date convert(String source) {
Date date = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
date = sdf.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
public String getPattern() {
return pattern;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
}
<bean id="myDateConverter" class="com.baizhiedu.converter.MyDateConverter">
<property name="pattern" value="yyyy-MM-dd"/>
bean>
ConversionSeviceFactoryBean
定义 id属性,值必须是 conversionService
;
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="myDateConverter"/>
set>
property>
bean>
Spring 框架其实内置了日期类型的转换器:日期格式必须是 2020/06/12
。
<bean id="good" class="com.baizhiedu.converter.Good">
<property name="name" value="zhenyu"/>
<property name="birthday" value="2012/12/12"/>
bean>
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return null; //Object bean, String beanName 类名 对象名
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Category) { //BeanPostProcessor 会对 Spring 工厂创建的所有对象进行加工
Category category = (Category) bean;
category.setName("baizhiedu");
return category;
}
return bean;
}
}
<bean id="myBeanPostProcessor" class="com.baizhiedu.beanpost.MyBeanPostProcessor"/>
静态代理:为每⼀个原始类,手工编写⼀个代理类(.java .class)
public interface UserService {
void register(User user);
boolean login(String name, String password);
}
public class UserServiceImpl implements UserService {
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register 业务运算 + DAO");
}
@Override
public boolean login(String name, String password) {
System.out.println("UserServiceImpl.login 业务运算 + DAO");
return true;
}
}
代理类是为原始类添加额外功能的
/**
* 静态代理类编码实现
*/
public class UserServiceProxy implements UserService { // 实现原始类相同的接口
private UserService userService = new UserServiceImpl(); // 代理类中必须有原始类
@Override
public void register(User user) {
System.out.println("---log---"); // 额外功能
userService.register(user);
}
@Override
public boolean login(String name, String password) {
System.out.println("---log---"); // 额外功能
return userService.login(name, password);
}
}
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>5.1.14.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjrtartifactId>
<version>1.8.9version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.8.13version>
dependency>
public interface UserService {
void register(User user);
boolean login(String name, String password);
}
public class UserServiceImpl implements UserService {
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register 业务运算 + DAO");
}
@Override
public boolean login(String name, String password) {
System.out.println("UserServiceImpl.login 业务运算 + DAO");
return true;
}
}
MethodBeforeAdvice
接口
public class Before implements MethodBeforeAdvice {
/**
* 作用: 把需要运行在原始方法执行之前运行的额外功能, 书写在 before 方法中
*/
@Override
public void before(Method method, Object[] objects, Object target) throws Throwable {
System.out.println("---method before advice log---");
}
}
<bean id="before" class="com.baizhiedu.aop.Before"/>
⽬的: 由程序员根据⾃⼰的需要,决定额外功能加入给哪个原始方法(register、login)
<aop:config>
<aop:pointcut id="pc" expression="execution(* * (..))"/>
aop:config>
<aop:config>
<aop:pointcut id="pc" expression="execution(* * (..))"/>
<aop:advisor advice-ref="before" pointcut-ref="pc"/>
aop:config>
//Spring 的工厂通过原始对象的 id 值获得的是代理对象
//获得代理对象后,可以通过声明接口类型,进行对象的存储
@Test
public void test() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.login("admin", "1234");
userService.register(new User());
}
动态字节码技术:通过第三方动态字节码框架,在 JVM 中创建对应类的字节码,进而创建对象,当虚拟机结束,动态字节码跟着消失。
结论:动态代理不需要定义类文件,都是 JVM 运行过程中动态创建的,所以不会造成类⽂件数量过多,影响项目管理的问题。
动态代理使得 额外功能的维护性大大增强
作用:需要把运行在原始方法执行之前运行的额外功能,书写在before方法中
Method: 额外功能所增加给的那个原始方法
login方法register方法
showOrder方法
Object[]: 额外功能所增加给的那个原始方法的参数。String name,String password
UserObject: 额外功能所增加给的那个原始对象 UserServiceImpl
OrderServiceImpl
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("-----new method before advice log------");
}
public class Around implements MethodInterceptor { //比如事务、抛出异常
@Override
public Object invoke(MethodInvocation Invocation) throws Throwable {
System.out.println("---额外功能运行在原始方法执行之前---");
Object ret = Invocation.proceed(); // 原始方法运行, 获取原始方法的返回值
System.out.println("---额外功能运行在原始方法执行之后---");
return ret;
}
}
<bean id="around" class="com.baizhiedu.dynamic.Around"/>
<aop:config>
<aop:pointcut id="pc" expression="execution(* * (..))"/>
<aop:advisor advice-ref="around" pointcut-ref="pc"/>
aop:config>
定义一个方法
public void add(int i, int j)
* * (..)
* * (..) --> 所有方法
* ---> 修饰符 返回值
* ---> 方法名
() ---> 参数表
.. ---> 对于参数没有要求 (参数有没有,参数有⼏个都行,参数是什么类型的都行)
<aop:pointcut id="pc" expression="execution(* login (..))"/>
<aop:pointcut id="pc" expression="execution(* register (..))"/>
精准方法切入点限定
<aop:pointcut id="pc" expression="execution(* com.baizhiedu.proxy.UserServiceImpl.login(..))"/>
<aop:pointcut id="pc" expression="execution(* com.baizhiedu.proxy.UserServiceImpl.login(String, String))"/>
# 类中所有的方法加入了额外功能
<aop:pointcut id="pc" expression="execution(* com.baizhiedu.proxy.UserServiceImpl.*(..))"/>
# 忽略包
1. 类只存在一级包
<aop:pointcut id="pc" expression="execution(* *.UserServiceImpl.*(..))"/>
2. 类存在多级包
<aop:pointcut id="pc" expression="execution(* *..UserServiceImpl.*(..))"/>
# 切入点包中的所有类,必须在proxy中,不能在proxy包的⼦包中
<aop:pointcut id="pc" expression="execution(* com.baizhiedu.proxy.*.*(..))"/>
# 切入点当前包及其⼦包都生效
<aop:pointcut id="pc" expression="execution(* com.baizhiedu.proxy..*.*(..))"/>
args
作用:主要用于 函数(方法) 参数的匹配;
切入点:方法参数必须得是 2 个字符串类型的参数
# 使用 execution
<aop:pointcut id="pc" expression="execution(* *(String, String))"/>
# 使用 args
<aop:pointcut id="pc" expression="args(String, String)"/>
within
作用:主要用于进行 类、包切入点表达式 的匹配。
切入点: UserServiceImpl 这个类
# 使用 execution
<aop:pointcut id="pc" expression="expression(* *..UserServiceImpl.*(..))"/>
# 使用 within
<aop:pointcut id="pc" expression="within(*..UserServiceImpl)"/>
切入点: com.baizhiedu.proxy 这个包
# 使用 execution
<aop:pointcut id="pc" expression="execution(* com.baizhiedu.proxy..*.*(..)"/>
# 使用 within
<aop:pointcut id="pc" expression="within(com.baizhiedu.proxy..*)"/>
作用:为具有特殊注解的 方法 加入额外功能。
@Target(ElementType.METHOD) //注解需要用在方法上面
@Retention(RetentionPolicy.RUNTIME) //注解什么时候起作用
public @interface Log {
}
<aop:pointcut id="pc" expression="@annotation(com.baizhiedu.Log)"/>
and 与操作
案例: 方法名叫 login 同时 参数是 2个字符串
# execution
<aop:pointcut id="pc" expression="execution(* login(String, String))"/>
# execution and args
<aop:pointcut id="pc" expression="execution(* login(..)) and args(String, String))"/>
注意:与操作不能⽤于同种类型的切⼊点函数
以下这个是错误的, 因为不存在同时叫 login 和 register 的方法
<aop:pointcut id="pc" expression="execution(* login(..)) and execution(* register(..))"/>
or 或操作:
案例: 方法名叫 register 或 login 的⽅法作为切⼊点
<aop:pointcut id="pc" expression="execution(* login(..)) or execution(* register(..))"/>
AOP (Aspect Oriented Programing)
⾯向切⾯编程 = Spring动态代理开发
以切⾯为基本单位的程序开发,通过切⾯间的彼此协同,相互调⽤,完成程序的构建
切⾯ = 切⼊点 + 额外功能
OOP (Object Oritened Programing)
⾯向对象编程 Java
以对象为基本单位的程序开发,通过对象间的彼此协同,相互调⽤,完成程序的构建
POP (Producer Oriented Programing)
⾯向过程(⽅法、函数)编程 C
以过程为基本单位的程序开发,通过过程间的彼此协同,相互调⽤,完成程序的构建
AOP的概念:
本质就是Spring得动态代理开发,通过代理类为原始类增加额外功能。
好处:利于原始类的维护
注意:AOP编程不可能取代OOP,OOP编程有意补充。
public class TestJDKProxy {
/*
1. 借用类加载器 TestJDKProxy
UserServiceImpl
2. JDK8.x前
final UserService userService = new UserServiceImpl();
*/
public static void main(String[] args) {
//1 创建原始对象
UserService userService = new UserServiceImpl();
//2 JDK创建动态代理
InvocationHandler handler = new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------proxy log --------");
Object ret = method.invoke(userService, args); //原始方法运行
return ret;
}
};
UserService userServiceProxy = (UserService)Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(),userService.getClass().getInterfaces(),handler);
userServiceProxy.login("suns", "123456");
userServiceProxy.register(new User());
}
}
CGlib创建动态代理的原理:⽗⼦继承关系创建代理对象,原始类作为⽗类,代理类作为⼦类,这样既可以保证2者⽅法⼀致,同时在代理类中提供新的实现(额外功能+原始⽅法)
public class UserService {
public void login(String name, String password) {
System.out.println("UserService.login");
}
public void register(User user) {
System.out.println("UserService.register");
}
}
package com.baizhiedu.cglib;
import com.baizhiedu.proxy.User;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class TestCglib {
public static void main(String[] args) {
//1 创建原始对象
UserService userService = new UserService();
/*
2 通过cglib方式创建动态代理对象
Proxy.newProxyInstance(classloader,interface,invocationhandler)
Enhancer.setClassLoader()
Enhancer.setSuperClass()
Enhancer.setCallback(); ---> MethodInterceptor(cglib)
Enhancer.create() ---> 代理
*/
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(TestCglib.class.getClassLoader());
enhancer.setSuperclass(userService.getClass());
MethodInterceptor interceptor = new MethodInterceptor() {
//等同于 InvocationHandler --- invoke
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("---cglib log----");
Object ret = method.invoke(userService, args);
return ret;
}
};
enhancer.setCallback(interceptor);
UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.login("suns", "123345");
userServiceProxy.register(new User());
}
}
思路分析:主要通过 BeanPostProcessor
将原始对象加工为代理对象
public class ProxyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
/*
Proxy.newProxyInstance();
*/
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("----- new Log-----");
Object ret = method.invoke(bean, args);
return ret;
}
};
return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(),bean.getClass().getInterfaces(),handler);
}
}
# 通过切⾯类 定义了 额外功能 @Around
定义了 切⼊点 @Around(“execution(* login(…))”)
@Aspect 切⾯类
/*
1. 额外功能
public class MyArround implements MethodInterceptor{
public Object invoke(MethodInvocation invocation){
Object ret = invocation.proceed();
return ret;
}
}
2. 切入点
*/
@Aspect
public class MyAspect {
@Around("execution(* login(..))")
public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("----aspect log ------");
Object ret =joinPoint.proceed();
return ret;
}
}
<bean id="userService" class="com.baizhiedu.aspect.UserServiceImpl"/>
<bean id="arround" class="com.baizhiedu.aspect.MyAspect"/>
<aop:aspectj-autoproxy proxy-target-class="true" />
切入点复用
切入点复用:在切面类中定义⼀个函数,上面用 @Pointcut
注解。
通过这种方式定义切入点表达式,后续更加有利于切入点复用。
@Aspect
public class MyAspect {
@Pointcut("execution(* *..UserServiceImpl.*(..))")
public void myPointcut(){}
@Around(value="myPointcut()")
public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("----aspect log ------");
Object ret = joinPoint.proceed();
return ret;
}
@Around(value="myPointcut()")
public Object arround1(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("----aspect tx ------");
Object ret = joinPoint.proceed();
return ret;
}
}
默认情况 AOP 编程 底层应用 JDK动态代理创建方式。
基于注解的 AOP 开发 中切换为 Cglib:
<aop:aspectj-autoproxy proxy-target-class="true"/>
传统的 AOP 开发 中切换为 Cglib:
<aop:config proxy-target-class="true">
...
aop:config>
public class UserServiceImpl implements UserService, ApplicationContextAware {
private ApplicationContext ctx;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx = applicationContext;
}
@Log
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register 业务运算 + DAO ");
//throw new RuntimeException("测试异常");
//调用的是原始对象的login方法 ---> 核心功能
/*
设计目的:代理对象的login方法 ---> 额外功能+核心功能
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext2.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.login();
Spring工厂重量级资源 一个应用中 应该只创建一个工厂
*/
UserService userService = (UserService) ctx.getBean("userService");
userService.login("suns", "123456");
}
@Override
public boolean login(String name, String password) {
System.out.println("UserServiceImpl.login");
return true;
}
}
Object ret = joinPoint.proceed();
return ret;
}
}
**默认情况 AOP 编程 底层应用 JDK动态代理创建方式**。
基于注解的 AOP 开发 中切换为 Cglib:
```xml
传统的 AOP 开发 中切换为 Cglib:
<aop:config proxy-target-class="true">
...
aop:config>
public class UserServiceImpl implements UserService, ApplicationContextAware {
private ApplicationContext ctx;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx = applicationContext;
}
@Log
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register 业务运算 + DAO ");
//throw new RuntimeException("测试异常");
//调用的是原始对象的login方法 ---> 核心功能
/*
设计目的:代理对象的login方法 ---> 额外功能+核心功能
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext2.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.login();
Spring工厂重量级资源 一个应用中 应该只创建一个工厂
*/
UserService userService = (UserService) ctx.getBean("userService");
userService.login("suns", "123456");
}
@Override
public boolean login(String name, String password) {
System.out.println("UserServiceImpl.login");
return true;
}
}