一.代理模式
1.静态代理
角色分析:
- 抽象角色: - -般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一-般会做- -些附属操作
- 客户:访问代理对象的人!
代码实现
1.实现共同的接口:出租
2.房东要做出租这件事情
如果没有中介,我们直接去找房东租房:
3.现在有了代理中介
中介可以具备房东的功能,同时还会有其他附属的功能,带看房,收租金
4.那么客户就可以直接找中介租房,而不需要找房东,房东也只是把房子挂出去就不需要管别的
代理模式的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一 些公共的业务
- 公共也就就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
缺点:
- 一个真实角色就会产生-个代理角色;代码量会翻倍~开发效率会变低
2.动态代理
-
动态代理和静态代理角色一样
-
动态代理的代理类是动态生成的,不是我们直接写好的!
-
动态代理分为两大类:基于接口的动态代理,基于类的动态代理
基于接口— JDK动态代理[我们在这里使用]
基于类: cglib
java字节码实现: javasist
实现动态代理需要了解两个类:Proxy 代理;InvocationHandle:调用处理程序
InvocationHandle
Proxy
代码实现
前面的Rent和Host不变,这时候我们创建一个动态代理类
package com.ycm.demeo02;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//自动生成代理类
public class ProxyInvocationHandle implements InvocationHandler {
//被代理的接口
private Object target;
public void setRent(Object target) {
this.target = target;
}
/**
* Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
* new Class[] { Foo.class },
* handler);
*/
public Object getProxy(){
/**
参数:
* 这个类本身
* 实现的接口
* InvocationHandler,由于这个类实现InvocationHandler接口,所以就是本身
*/
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
watchHouse();
Object result = method.invoke(target, args);
fare();
return result;
}
public void watchHouse(){
System.out.println("中介带看房子");
}
public void fare(){
System.out.println("中介收房租");
}
}
动态代理的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共也就就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口
客户层面:
现在代理类不是写好的,而是根据需要动态创建的,动态代理使用的就是反射技术。
AOP
浅谈aop
1.什么是AOP
AOP (Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOPOOP的延续,是软件开发中的一个热点,也是Spring框架中的一一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
通过aop,可以做到不改变原先逻辑代码,将要加入的业务逻辑从原先的逻辑代码切面插入进去
2.AOP在Spring中的作用
提供声明式事务,允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…
- 切面(ASPECT) :横切关注点被模块化的特殊对象。即,它是一个类。要切入的类
- 通知(Advice) :切面必须要完成的工作。即,它是类中的一个方法。切入类中的一个方法
- 目标(Target) :被通知对象。 一个接口
- 代理(Proxy) :向目标对象应用通知之后创建的对象。 生成的代理类
- 切入点. (PointCut) :切面通知执行的“地点"的定义。 执行的地方
- 连接点(JointPoint) :与切入点匹配的执行点。 执行的地方
在SpringAOP中,通过Advice定义横逻辑,有5种类型的Advice
3.使用Spring实现AOP
方式一:使用Spring的API接口实现
1.创建接口
实现类
要插入的类
在方法执行前调用
在方法执行后调用的类
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean类-->
<bean class="com.ycm.service.UserServiceImpl" id="userService"/>
<bean class="com.ycm.log.Log" id="log"/>
<bean class="com.ycm.log.AfterLog" id="afterLog"/>
<!--配置aop-->
<aop:config>
<!--切入点:expression :要执行的位置-->
<!--execution(修饰符 返回值 包名.类名/接口名.方法名(参数列表))
(..)可以代表所有参数,(*)代表一个参数,(*,String)代表第一个参数为任何值,第二个参数为String类型.-->
<aop:pointcut id="pointcut" expression="execution( * com.ycm.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加-->
<!--把Log这个类切入到pointcut 方法里-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
测试
方式二:自定义来实现AOP(定义切面)
定义一个自定义类
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean类-->
<bean class="com.ycm.service.UserServiceImpl" id="userService"/>
<bean class="com.ycm.log.Log" id="log"/>
<bean class="com.ycm.log.AfterLog" id="afterLog"/>
<!--方式二-->
<!--定义类-->
<bean class="com.ycm.diy.DiyClass" id="diyClass"/>
<!--自定义切面-->
<aop:config>
<aop:aspect ref="diyClass">
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.ycm.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>
方式三. 使用注解实现AOP
在配置文件种开启注解支持
spring 整合mybatis
方式一
1.编写数据源配置
<!--通过spring管理数据源 dataSource-->
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</bean>
对比mybatis配置文件:
2.sqlSessionFactory
<!--SQlSessionFactory-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
<property name="dataSource" ref="dataSource"/>
<!--绑定mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="com/ycm/mapper/UserMapper.xml"/>
</bean>
对比mybatis:
3.sqlSessionTemplate
<!--sessionTemplate就是之前的SqlSession-->
<bean class="org.mybatis.spring.SqlSessionTemplate" id="sqlSession">
<!--使用构造器注入,因为它没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
对比mybatis
4.给接口加实现类
package com.ycm.mapper;
import com.ycm.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl implements UserMapper {
private SqlSessionTemplate sqlSession;
//实现set方法用于实现spring注入
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> getUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.getUser();
}
}
5.将自己写的实现类注入到spring容器中
<!--注入实现类-->
<bean class="com.ycm.mapper.UserMapperImpl" id="userMapper">
<property name="sqlSession" ref="sqlSession"/>
</bean>
测试:
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper userMapper = (UserMapper) context.getBean("userMapper");
List<User> user = userMapper.getUser();
System.out.println(user);
}
对比mybatis
方式二:
使用SqlSessionDaoSupport来获得一个sqlSession
查看源码,其实是这个类提前就获得了sqlSessionTemplate,本质上还是和方法一相同
在配置文件中注册
由上面的源码可以看到,这个类需要的是sqlSessionFactory,所以这里的参数传的就是sqlSessionFactory
使用Spring声明式事务
1.配置
在spring配置文件中创建一个对象
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource" />
</bean>
2.配置事务的通知
<!--配置事务的通知-->
<tx:advice transaction-manager="transactionManager" id="txAdvice">
<!--配置事务的传播特性-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
3.使用aop将事务织入
<!--配置事务的切入-->
<aop:config>
<aop:pointcut id="txPoint" expression="execution(* com.ycm.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
</aop:config>