前面的知识点 忘记的 参考
https://blog.csdn.net/weixin_44822455/article/details/108933150

Spring注解开发

1.1bean和属性注入

1 引入context约束

在xml文件中引入改文件即可

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:context="http://www.springframework.org/schema/context"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  6. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
  7. ">
  8. //上面在原来的spring xml文件中添加 context相关
  9. //这里配置需要扫描的包位置
  10. <context:component-scan base-package="com.pojo"></context:component-scan>
  11. <context:annotation-config></context:annotation-config>
  12. </beans>

2 编写实体类

  1. //这里component相当于 <bean id="user" class="com.pojo.user">
  2. @Component("user")
  3. public class User {
  4. //可以通过value直接实现注入
  5. @Value("小明")
  6. public String name ="zzz";
  7. }

3 测试

  1. public static void main(String[] args) {
  2. ApplicationContext ct = new ClassPathXmlApplicationContext("spring-01.xml");
  3. User user = (User)ct.getBean(User.class);
  4. System.out.println(user.name);
  5. }

2.Component衍生注解

我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!
@Component三个衍生注解
为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。

  • @Controller:web层
  • @Service:service层
  • @Repository:dao层

写上这些注解,就相当于将这个类交给Spring管理装配了!

3.小结

XML与注解比较

  • XML可以适用任何场景 ,结构清晰,维护方便
  • 注解不是自己提供的类使用不了,开发简单方便

xml与注解整合开发 :推荐最佳实践

  • xml管理Bean
  • 注解完成属性注入
  • 使用过程中, 可以不用扫描,扫描是为了类上的注解


作用:

  • 进行注解驱动注册,从而使注解生效
  • 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
  • 如果不扫描包,就需要手动配置bean
  • 如果不加注解驱动,则注入的值为null!

    代理模式

    静态代理

    静态代理角色分析

  • 抽象角色 : 一般使用接口或者抽象类来实现

  • 真实角色 : 被代理的角色
  • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
  • 客户 : 使用代理角色来进行一些操作 .

代码实现
1.创建出租的接口

  1. //抽象角色 :租房
  2. public interface Rent {
  3. public void rent();
  4. }

2.创建 房东,可以出租房子

  1. //真实角色:房东,房东要出租房子
  2. public class Host implements Rent {
  3. @Override
  4. public void rent() {
  5. System.out.println("房屋出租");
  6. }
  7. }

3.创建中介,代理房东出租房子,并且添加一些新的服务

  1. public class Proxy implements Rent {
  2. private Host host;
  3. public Proxy(){}
  4. public Proxy(Host host){
  5. this.host = host;
  6. }
  7. //租房
  8. @Override
  9. public void rent() {
  10. seeHouse();
  11. host.rent();
  12. fare();
  13. }
  14. //看房
  15. public void seeHouse(){
  16. System.out.println("带房客看房");
  17. }
  18. //收中介费
  19. public void fare(){
  20. System.out.println("收中介费");
  21. }
  22. }

4.创建客户类,进行买房子

  1. public class Clinet {
  2. public static void main(String[] args) {
  3. //房东要租房
  4. Host host = new Host();
  5. //中介帮助房东
  6. Proxy proxy = new Proxy(host);
  7. //你去找中介!
  8. proxy.rent();
  9. }
  10. }

静态代理的好处

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .

缺点 :

  • 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .

我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !

静态代理理解:在不动原来代码的基础上,需要增添新的业务,来实现功能拓展。

场景带入:若要在原有代码基础上,增添新的功能(添加日志功能),要求不改动原来代码
代码实现
1.创建接口

  1. public interface UserService {
  2. void add();
  3. void delete();
  4. void update();
  5. void select();
  6. }

2.创建原来代码

  1. public class UserServiceImpl implements UserService {
  2. @Override
  3. public void add() {
  4. System.out.println("添加了一个用户");
  5. }
  6. @Override
  7. public void delete() {
  8. System.out.println("删除了一个用户");
  9. }
  10. @Override
  11. public void update() {
  12. System.out.println("更改了一个用户");
  13. }
  14. @Override
  15. public void select() {
  16. System.out.println("查找了一个用户");
  17. }
  18. }

3.创建代理类,代理类 代理原来代码所需要实现功能,并增加日志功能

  1. public class UserServiceProxy implements UserService {
  2. UserService userService = new UserServiceImpl();
  3. public void setUserService(UserServiceImpl userService){
  4. this.userService = userService ;
  5. }
  6. @Override
  7. public void add() {
  8. log("add");
  9. userService.add();
  10. }
  11. @Override
  12. public void delete() {
  13. userService.delete();
  14. }
  15. @Override
  16. public void update() {
  17. userService.update();
  18. }
  19. @Override
  20. public void select() {
  21. userService.select();
  22. }
  23. public void log(String msg){
  24. System.out.println("[Debug] 使用了"+msg+"方法");
  25. }
  26. }

4.客户实现类

  1. public class Client {
  2. public static void main(String[] args) {
  3. UserServiceProxy userServiceProxy = new UserServiceProxy();
  4. UserServiceImpl userService = new UserServiceImpl();
  5. userServiceProxy.setUserService(userService);
  6. userServiceProxy.add();
  7. }
  8. }

原理图
image.png

动态代理

  • 动态代理的角色和静态代理的一样 .
  • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
  • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
    • 基于接口的动态代理——JDK动态代理
    • 基于类的动态代理—cglib
    • 现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
    • 我们这里使用JDK的原生代码来实现,其余的道理都是一样的!

需要了解2个类
JDK的动态代理需要了解两个类
核心 : InvocationHandler 和 Proxy , 打开JDK帮助文档看看
【InvocationHandler:调用处理程序】
Spring框架入门 - 图2
Object invoke(Object proxy, 方法 method, Object[] args);
//参数
//proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
【Proxy : 代理】
Spring框架入门 - 图3
Spring框架入门 - 图4
Spring框架入门 - 图5
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}

代码实现
还是以房屋出租为例子
1.房东类

  1. public class Host implements Rent {
  2. @Override
  3. public void rent() {
  4. System.out.println("房屋出租");
  5. }
  6. }

2.代理类

  1. public class ProxyInvocationHandler implements InvocationHandler {
  2. private Rent rent;
  3. public void setRent(Rent rent){
  4. this.rent = rent;
  5. };
  6. //生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色
  7. public Object getProxy(){
  8. return Proxy.newProxyInstance(this.getClass().getClassLoader(),
  9. rent.getClass().getInterfaces(),this);
  10. }
  11. // proxy : 代理类 method : 代理类的调用处理程序的方法对象.
  12. // 处理代理实例上的方法调用并返回结果
  13. @Override
  14. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  15. seeHouse();
  16. //核心:本质利用反射实现!
  17. Object result = method.invoke(rent, args);
  18. fare();
  19. return result;
  20. }
  21. //看房
  22. public void seeHouse(){
  23. System.out.println("带房客看房");
  24. }
  25. //收中介费
  26. public void fare(){
  27. System.out.println("收中介费");
  28. }
  29. }

3.客户实现类(测试类)

  1. public class Client {
  2. public static void main(String[] args) {
  3. //真实角色
  4. Host host = new Host();
  5. //代理实例的调用处理程序
  6. ProxyInvocationHandler pih = new ProxyInvocationHandler();
  7. pih.setRent(host); //将真实角色放置进去!
  8. Rent proxy = (Rent)pih.getProxy(); //动态生成对应的代理类!
  9. proxy.rent();
  10. }
  11. }
  1. 将代理类抽象 变成代理工具类 ```java public class ProxyInvocationHandler implements InvocationHandler { private Object target;

    public void setTarget(Object target) {

    1. this.target = target;

    }

    //生成代理类 public Object getProxy(){

    1. return Proxy.newProxyInstance(this.getClass().getClassLoader(),
    2. target.getClass().getInterfaces(),this);

    }

    // proxy : 代理类 // method : 代理类的调用处理程序的方法对象. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    1. log(method.getName());
    2. Object result = method.invoke(target, args);
    3. return result;

    }

    public void log(String methodName){

    1. System.out.println("执行了"+methodName+"方法");

    }

}

  1. 测试类
  2. ```java
  3. public class Test {
  4. public static void main(String[] args) {
  5. //真实对象
  6. UserServiceImpl userService = new UserServiceImpl();
  7. //代理对象的调用处理程序
  8. ProxyInvocationHandler pih = new ProxyInvocationHandler();
  9. pih.setTarget(userService); //设置要代理的对象
  10. UserService proxy = (UserService)pih.getProxy(); //动态生成代理类!
  11. proxy.delete();
  12. }
  13. }

动态代理好处
静态代理有的它都有,静态代理没有的,它也有!

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .
  • 一个动态代理 , 一般代理某一类业务
  • 一个动态代理可以代理多个类,代理的是接口!

    AOP

    定义

    AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
    Spring框架入门 - 图6

    AOP在Spring中的作用

    提供声明式事务;允许用户自定义切面
    Spring框架入门 - 图7
    SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
    Spring框架入门 - 图8
    即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .

    AOP的使用

    代码实现
    1.导入依赖

    1. <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    2. <dependency>
    3. <groupId>org.aspectj</groupId>
    4. <artifactId>aspectjweaver</artifactId>
    5. <version>1.9.4</version>
    6. </dependency>

    2.创建业务接口和业务类 ```java public interface UserService {

    public void add();

    public void delete();

    public void update();

    public void search();

}

  1. 业务实现类
  2. ```java
  3. public class UserService2 implements com.service.UserService2 {
  4. @Override
  5. public void add() {
  6. System.out.println("实现添加方法");
  7. }
  8. @Override
  9. public void delete() {
  10. System.out.println("实现删除方法");
  11. }
  12. @Override
  13. public void update() {
  14. System.out.println("实现更新方法");
  15. }
  16. @Override
  17. public void select() {
  18. System.out.println("实现查找方法");
  19. }
  20. }

3.配置文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:aop="http://www.springframework.org/schema/aop"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/aop
  8. http://www.springframework.org/schema/aop/spring-aop.xsd">
  9. //这里要留意新添加的aop引用文件
  10. <!-- 注册bean-->
  11. <bean id="userService" class="com.service.Impl.UserService2"></bean>
  12. <bean id="diy" class="com.util.DiyPointcut"></bean>
  13. <!-- 配置AOP-->
  14. <aop:config>
  15. <!-- 配置切入点-->
  16. <!-- 这里expression代码规范是这样的-->
  17. <aop:pointcut id="pointcut" expression="execution(* com.service.UserService2.*(..))"/>
  18. <!-- 配置切入面-->
  19. <!-- 这里ref代表准备要切入的类和方法-->
  20. <aop:aspect ref="diy">
  21. <aop:before pointcut-ref="pointcut" method="before"/>
  22. <aop:after pointcut-ref="pointcut" method="after"/>
  23. </aop:aspect>
  24. </aop:config>
  25. </beans>

4.测试类

  1. @Test
  2. public void test02(){
  3. ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop02.xml");
  4. UserService2 userService = (UserService2) context.getBean("userService");
  5. userService.add();
  6. }
  7. //结果入下
  8. //---------方法执行前---------
  9. //实现添加方法
  10. //---------方法执行后---------

AOP注解使用

代码实现
1.创建接口和实现类

  1. public interface UserService {
  2. public void doSth();
  3. }

实现类

  1. @Service
  2. public class UserServiceImpl implements UserService {
  3. @Override
  4. public void doSth() {
  5. System.out.println("开启业务");
  6. }
  7. }

2.要注入的一些其他方法类

  1. //这里也要使用Component将 方法类托管给Spring容器,同时也要用注解Aspect标注切面类
  2. @Component
  3. @Aspect
  4. public class MyAspect {
  5. //**这里可以使用定义切点方式(补充)
  6. @Pointcut("execution(* com.demo02.UserServiceImpl.do*(..))")
  7. public void pointcut1(){
  8. //这里不需要写东西
  9. }
  10. //之后@Before("pointcut1()")
  11. //这里代表是切面 执行于切点前还是切点后 其他方法类似 (固定写法)
  12. @Before("execution(* com.demo02.UserServiceImpl.do*(..))")
  13. public void before(){
  14. System.out.println("=======业务前注入======");
  15. }
  16. }

3.配置类

  1. //扫描spring相关类 和包
  2. //开启注解允许
  3. //标注这是配置类
  4. @Configuration
  5. @EnableAspectJAutoProxy
  6. @ComponentScan("com.demo02")
  7. public class Myconfig {
  8. }

4.测试类

  1. public class MyTest {
  2. @Test
  3. public void test07(){
  4. ApplicationContext context = new AnnotationConfigApplicationContext(Myconfig.class);
  5. // 这里需要注意 需要获得的是 接口.class 而不是实现类
  6. UserService bean = context.getBean(UserService.class);
  7. bean.doSth();
  8. }
  9. }

测试结果

  1. =======业务前注入======
  2. 开启业务