一文学会Spring

一、Spring简介

Spring的优点

  1. Spring是一个开源免费的框架、容器
  2. Spring是一个轻量级的框架,非侵入式的
  3. 控制反转IOC、面向切面AOP
  4. 支持事务

Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器

二、IOC

2.1 IOC本质

控制反转IOC,是一种设计思想,DI(依赖注入)是实现IOC的一种方式

通俗点来说就是原本你需要自己创建对象(new),现在交给Spring来管理。你只需要告诉Spring需要什么对象,Spring就会帮你创建并注入。

在这里插入图片描述

Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从IOC容器中取出需要的对象。

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IOC容器,其实现方式是依赖注入(DI)

2.2 IOC创建对象方式

2.2.1 通过无参构造方法来创建

代码示例

1、创建user类

public class User {
    private String name;
    public User() {
        System.out.println("user无参构造方法");
    }
    public void setName(String name) {
        this.name = name;
    }
    public void show(){
        System.out.println("name="+ name );
    }
}

2、编辑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">

   <bean id="user" class="com.kuang.pojo.User">
       <property name="name" value="kuangshen"/>
   </bean>

</beans>

3、测试类

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
user.show();

我们会发现在我们调用show()方法的时候,User对象已经通过无参构造初始化了。

注意:当我们的方法中没有无参构造方法会报错

在这里插入图片描述

2.2.2通过有参构造方法来创建

user类中添加有参构造方法

public User(String name) {
    this.name=name;
}

有参构造我们有三种方式来编写(在beans.xml中)

  1. 根据下标

    <bean id="user" class="com.kuang.pojo.User">
       <!-- index指构造方法 , 下标从0开始 -->
       <constructor-arg index="0" value="kuangshen2"/>
    </bean>
    
  2. 根据参数名字

    <bean id="user" class="com.kuang.pojo.User">
       <!-- name指参数名 -->
       <constructor-arg name="name" value="kuangshen2"/>
    </bean>
    
  3. 根据参数类型

    <bean id="user" class="com.kuang.pojo.User">
       <constructor-arg type="java.lang.String" value="kuangshen2"/>
    </bean>
    

三、Spring配置

3.1 别名

通过alias设置别名,为bean设置别名,可以设置多个别名

<alias name="user" alias="LTY"></alias>

3.2 Bean的配置

id:bean的唯一标识,相当于我们的对象名

class:bean对象所对应的全限定名

name:也是别名,可以同时取多个别名

3.3 import

一般用于团队开发使用,可以将两个或多个不同的配置文件,合并为一个配置文件

<import resource="beans.xml"/>
<import resource="beans1.xml"/>

四、依赖注入(DI)

依赖:指bean对象的创建依赖于容器

注入:指bean对象所依赖的资源,由容器来设置和装配

4.1 构造器注入

我们上面的案例就是构造器注入(构造器注入必须要有有参构造,可以没有无参构造)

4.2 set注入(重点)

要求被注入的属性,必须要有set方法

准备代码

public class Address {
    private String address;
    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
public class Student {
 
     private String name;
     private Address address;
     private String[] books;
     private List<String> hobbys;
     private Map<String,String> card;
     private Set<String> games;
     private String wife;
     private Properties info;
 
     public void setName(String name) {
         this.name = name;
    }
 
     public void setAddress(Address address) {
         this.address = address;
    }
 
     public void setBooks(String[] books) {
         this.books = books;
    }
 
     public void setHobbys(List<String> hobbys) {
         this.hobbys = hobbys;
    }
 
     public void setCard(Map<String, String> card) {
         this.card = card;
    }
 
     public void setGames(Set<String> games) {
         this.games = games;
    }
 
     public void setWife(String wife) {
         this.wife = wife;
    }
 
     public void setInfo(Properties info) {
         this.info = info;
    }
 
     public void show(){
         System.out.println("name="+ name
                 + ",address="+ address.getAddress()
                 + ",books="
        );
         for (String book:books){
             System.out.print("<<"+book+">>\t");
        }
         System.out.println("\n爱好:"+hobbys);
 
         System.out.println("card:"+card);
 
         System.out.println("games:"+games);
 
         System.out.println("wife:"+wife);
 
         System.out.println("info:"+info);
 
    }
 }
  1. 常量注入

     <bean id="student" class="com.kuang.pojo.Student">
         <property name="name" value="小明"/>
     </bean>
    
  2. bean注入

     <bean id="addr" class="com.kuang.pojo.Address">
         <property name="address" value="重庆"/>
     </bean>
     
     <bean id="student" class="com.kuang.pojo.Student">
         <property name="name" value="小明"/>
         <property name="address" ref="addr"/>
     </bean>
    
  3. 数组注入

     <bean id="student" class="com.kuang.pojo.Student">
         <property name="name" value="小明"/>
         <property name="address" ref="addr"/>
         <property name="books">
             <array>
                 <value>西游记</value>
                 <value>红楼梦</value>
                 <value>水浒传</value>
             </array>
         </property>
     </bean>
    
    
  4. List注入

     <property name="hobbys">
         <list>
             <value>听歌</value>
             <value>看电影</value>
             <value>爬山</value>
         </list>
     </property>
    1234567
    
  5. Map注入

     <property name="card">
         <map>
             <entry key="中国邮政" value="456456456465456"/>
             <entry key="建设" value="1456682255511"/>
         </map>
     </property>
    123456
    
  6. set注入

     <property name="games">
         <set>
             <value>LOL</value>
             <value>BOB</value>
             <value>COC</value>
         </set>
     </property>
    1234567
    
  7. Null注入

     <property name="wife"><null/></property>
    
  8. Properties注入

     <property name="info">
         <props>
             <prop key="学号">20190604</prop>
             <prop key="性别"></prop>
             <prop key="姓名">小明</prop>
         </props>
     </property>
    

4.3 p命名和c命名

  1. P命名空间注入 : 需要在头文件中加入约束文件

    导入约束 : xmlns:p="http://www.springframework.org/schema/p"
    <!--P(属性: properties)命名空间 , 直接注入属性-->
    <bean id="user" class="com.kuang.pojo.User" p:name="狂神" p:age="18"/>
    
  2. c 命名空间注入 : 需要在头文件中加入约束文件

    导入约束 : xmlns:c="http://www.springframework.org/schema/c"
    <!--C(构造: Constructor)命名空间 , 使用构造器注入-->
    <bean id="user" class="com.kuang.pojo.User" c:name="狂神" c:age="18"/>
    

4.4 Bean的作用域

那些组成应用程序的主体及由Spring IOC容器所管理的对象,被称之为bean。

bean就是由IOC容器初始化、装配及管理的对象。

在这里插入图片描述

singleton(单例模式)

当一个bean的作用域为singleton时,那么Spring IOC容器只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则会返回bean的同一实例。

 <bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">

测试

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
     User user = (User) context.getBean("user");
     User user2 = (User) context.getBean("user");
     System.out.println(user==user2);//返回true

Prototype(原型模式)

当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求时都会创建一个新的bean实例。

 <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定义创建而成

 <bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>
Session

当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。

 <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

五、Bean的自动装配

Spring中bean有三种装配机制,分别是:

  1. 在xml中显示配置
  2. 在java中显示配置
  3. 隐式的bean发现机制和自动装配

Spring的自动装配需要从两个角度来实现,或者说是两个操作:

  1. 组件扫描:spring会自动发现应用上下文中所创建的bean
  2. 自动装配:soring自动满足bean之间的依赖,也就是我们说的IOC/DI

5.1 byName(按名称自动装配)

修改bean配置,增加一个属性autowire="byName"

<bean id="user" class="com.kuang.pojo.User" autowire="byName">
   <property name="str" value="qinjiang"/>
</bean>
  1. 将查找其类中所有的set方法名,例如setCat,获得将set去点并且首字母小写的字符串,即cat。
  2. 去spring容器中寻找是否有此字符串名称id的对象
  3. 如果有,就注入输出;如果没有,就报空指针异常

5.2 byType(按类型自动装配)

使用byType首先要保证:同一类型的对象,在spring容器中唯一;如果不唯一,会报异常。

<bean id="user" class="com.kuang.pojo.User" autowire="byType">
   <property name="str" value="qinjiang"/>
</bean>

5.3 使用注解

jdk1.5开始支持注解,spring2.5开始全面支持注解。

  1. 在spring配置文件中引入context文件头

    xmlns:context="http://www.springframework.org/schema/context"
    
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    
  2. 开启属性注解支持

    <context:annotation-config/>
    

@Autowired

  • @Autowired是按类型装配的,不支持id匹配
  • 需要导入spring-aop的包
public class User {
   @Autowired
   private Cat cat;
   @Autowired
   private Dog dog;
   private String str;

   public Cat getCat() {
       return cat;
  }
   public Dog getDog() {
       return dog;
  }
   public String getStr() {
       return str;
  }
}

@Qualifier

  • @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
  • @Qualifier不能单独使用
@Autowired
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
<bean id="dog1" class="com.kuang.pojo.Dog"/>
<bean id="dog2" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>

@Resourse

  • @Resource如果有指定的neme属性,先按该属性进行byName方式查找装配
  • 其次再进行默认的byName方式进行装配
  • 如果以上都不成功,则按byType进行装配
  • 都不成功,报异常
public class User {
   //如果允许对象为null,设置required = false,默认为true
   @Resource(name = "cat2")
   private Cat cat;
   @Resource
   private Dog dog;
   private String str;
}
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>

<bean id="user" class="com.kuang.pojo.User"/>

@Autowired与@Resource的区别

  1. @Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
  2. @Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如果想要使用名称装配可以结合@Qualifier注解进行使用。
  3. @Resource默认按名称装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。

六、使用注解开发

在spring4之后,想要使用注解形式,必须引入aop的包

在配置文件中,还要引入一个context约束

6.1 Bean的实现

@Component(“xxxx”)

  1. 配置扫描哪些包下的注解

    <!--指定注解扫描包-->
    <context:component-scan base-package="com.kuang.pojo"/>
    
  2. 在指定包下编写类。增加注解

    @Component("user")
    // 相当于配置文件中 <bean id="user" class="当前注解的类"/>
    public class User {
       public String name = "秦疆";
    }
    

6.2 属性注入

@value

  1. 可以不用提供set方法,直接在对象名上添加@value(“值”)

    @Component("user")
    // 相当于配置文件中 <bean id="user" class="当前注解的类"/>
    public class User {
       @Value("秦疆")
       // 相当于配置文件中 <property name="name" value="秦疆"/>
       public String name;
    }
    
  2. 如果提供了set方法,在set方法上添加@value(“值”)

    @Component("user")
    public class User {
    
       public String name;
    
       @Value("秦疆")
       public void setName(String name) {
           this.name = name;
      }
    }
    

6.3 衍生注解

@Component三个衍生注解

为了更好的进行分层,spring可以使用其他三个注解,功能一样。

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

6.4 作用域

@scope

  • singleton:默认的,spring会采用单例模式创建这个对象。关闭工程,所有对象都会销毁
  • prototype:多例模式。关闭工厂,所有对象不会销毁。内部的垃圾回收机制会回收。
@Controller("user")
@Scope("prototype")
public class User {
   @Value("秦疆")
   public String name;
}

6.4 小结

XML与注解比较

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

XML与注解整合开发(推荐):

  • xml管理bean

  • 注解完成属性注入

  • 使用过程中,可以不用扫描,扫描是为了类上的注解

    <context:annotation-config/>  
    

七、基于Java类进行配置

@Configuration

  1. 新建一个config配置包,编写一个MyConfig配置类

    @Configuration  //代表这是一个配置类
    public class MyConfig {
    
       @Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
       public Dog dog(){
           return new Dog();
      }
    
    }
    

导入其他配置如何做呢?

@Import

  1. 我们再编写一个配置类

    @Configuration  //代表这是一个配置类
    public class MyConfig2 {
    }
    
  2. 再之前的配置类中我们来选择导入这个配置类

    @Configuration
    @Import(MyConfig2.class)  //导入合并其他配置类,类似于配置文件中的 inculde 标签
    public class MyConfig {
    
       @Bean
       public Dog dog(){
           return new Dog();
      }
    
    }
    

八、代理模式

8.1 静态代理

静态代理角色分析

  • 抽象角色:一般使用接口或者抽象类来实现
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色;代理真实角色后,一般会做一些附属的操作
  • 客户:使用代理角色来进行一些操作

静态代理模式的核心思想

  • 代理与正式角色实现同一接口
  • 代理持有真实角色的引用
  • 代理增强角色

代码实现

Rent . java 即抽象角色

//抽象角色:租房
public interface Rent {
   public void rent();
}
1234

Host . java 即真实角色

//真实角色: 房东,房东要出租房子
public class Host implements Rent{
   public void rent() {
       System.out.println("房屋出租");
  }
}
123456

Proxy . java 即代理角色

//代理角色:中介
public class Proxy implements Rent {

   private Host host;
   public Proxy() { }
   public Proxy(Host host) {
       this.host = host;
  }

   //租房
   public void rent(){
       seeHouse();
       host.rent();
       fare();
  }
   //看房
   public void seeHouse(){
       System.out.println("带房客看房");
  }
   //收中介费
   public void fare(){
       System.out.println("收中介费");
  }
}
123456789101112131415161718192021222324

Client . java 即客户

//客户类,一般客户都会去找代理!
public class Client {
   public static void main(String[] args) {
       //房东要租房
       Host host = new Host();
       //中介帮助房东
       Proxy proxy = new Proxy(host);

       //你去找中介!
       proxy.rent();
  }
}

分析:真实角色只需关注核心业务,代理角色负责增强功能

静态代理的好处优缺点

优点

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

缺点

  • 类多了,多了代理类,工作量变大

8.2 动态代理

动态代理的核心思想是通过反射机制接口实现,在程序运行期间动态创建代理对象。

动态代理的角色和静态代理的一样

动态代理的代理类时动态生成的 静态代理的代理类是我们提前写好的

动态代理分为两类:一类是基于接口动态代理,一类是基于类的动态代理

  • 基于接口的动态代理–JDK动态代理
  • 基于类的动态代理–cglib
  • 现在用的比较多的是javasist来生成动态代理

JDK动态代理

代理对象会拦截所有接口方法的调用,并将其委托给InvocationHandlerinvoke 方法

动态代理的核心是方法调用的拦截和转发

//万能公式
public class ProxyInvocationHandler implements InvocationHandler {
   private Object target;

   public void setTarget(Object target) {
       this.target = target;
  }

   //生成代理类
   public Object getProxy(){
       return Proxy.newProxyInstance(this.getClass().getClassLoader(),
               target.getClass().getInterfaces(),this);
  }

   // proxy : 代理类
   // method : 代理类的调用处理程序的方法对象.
   public Object invoke(Object proxy, Method method, Object[] args) throwsThrowable {
       log(method.getName());
       Object result = method.invoke(target, args);
       return result;
  }

   public void log(String methodName){
       System.out.println("执行了"+methodName+"方法");
  }

}

测试

public class Test {
   public static void main(String[] args) {
       //真实对象
       UserServiceImpl userService = new UserServiceImpl();
       //代理对象的调用处理程序
       ProxyInvocationHandler pih = new ProxyInvocationHandler();
       pih.setTarget(userService); //设置要代理的对象
       UserService proxy = (UserService)pih.getProxy(); //动态生成代理类!
       proxy.delete();
  }
}

九、AOP

AOP就是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

9.1 AOP在spring中的作用

AOP核心概念

  1. 横切关注点(Cross-Cutting Concerns):多个模块中重复出现的、与核心业务无关的功能

  2. 切面(Aspect):封装横切关注点的模块,包含通知(Advice)切点(Pointcut)

  3. 连接点(Join Point):程序执行过程中可以插入切点的面,例如方法调用、异常抛出等

  4. 切点(Pointcut):通过表达式匹配一组连接点,确定在哪些地方通知

    示例:execution(* com.example.service.*.*(..)) 匹配 service 包下所有类的所有方法。

  5. 通知(Advice):切面在特定连接点执行的动作

    img

9.2 使用Spring实现AOP

重点:使用AOP,需要导入一个依赖包

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.4</version>
</dependency>

9.2.1 通过Spring API实现

首先编写我们的业务接口和实现类

public interface UserService {

   public void add();

   public void delete();

   public void update();

   public void search();

}
public class UserServiceImpl implements UserService{

   @Override
   public void add() {
       System.out.println("增加用户");
  }

   @Override
   public void delete() {
       System.out.println("删除用户");
  }

   @Override
   public void update() {
       System.out.println("更新用户");
  }

   @Override
   public void search() {
       System.out.println("查询用户");
  }
}

然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强

public class Log implements MethodBeforeAdvice {

   //method : 要执行的目标对象的方法
   //objects : 被调用的方法的参数
   //Object : 目标对象
   @Override
   public void before(Method method, Object[] objects, Object o) throws Throwable {
       System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");
  }
}
public class AfterLog implements AfterReturningAdvice {
   //returnValue 返回值
   //method被调用的方法
   //args 被调用的方法的对象的参数
   //target 被调用的目标对象
   @Override
   public void afterReturning(Object returnValue, Method method, Object[] args,Object target) throws Throwable {
       System.out.println("执行了" + target.getClass().getName()
       +"的"+method.getName()+"方法,"
       +"返回值:"+returnValue);
  }
}
12345678910111213141516171819202122

最后去spring的文件中注册 , 并实现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
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

   <!--注册bean-->
   <bean id="userService" class="com.kuang.service.UserServiceImpl"/>
   <bean id="log" class="com.kuang.log.Log"/>
   <bean id="afterLog" class="com.kuang.log.AfterLog"/>

   <!--aop的配置-->
   <aop:config>
       <!--切入点 expression:表达式匹配要执行的方法-->
       <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
       <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
       <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
       <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
   </aop:config>

</beans>

测试

public class MyTest {
   @Test
   public void test(){
       ApplicationContext context = newClassPathXmlApplicationContext("beans.xml");
       UserService userService = (UserService) context.getBean("userService");
       userService.search();
  }
}

9.2.2 自定义类来实现AOP

第一步 : 写我们自己的一个切入类

public class DiyPointcut {

   public void before(){
       System.out.println("---------方法执行前---------");
  }
   public void after(){
       System.out.println("---------方法执行后---------");
  }
   
}

去spring中配置

<!--第二种方式自定义实现-->
<!--注册bean-->
<bean id="diy" class="com.kuang.config.DiyPointcut"/>

<!--aop的配置-->
<aop:config>
   <!--第二种方式:使用AOP的标签实现-->
   <aop:aspect ref="diy">
       <aop:pointcut id="diyPonitcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
       <aop:before pointcut-ref="diyPonitcut" method="before"/>
       <aop:after pointcut-ref="diyPonitcut" method="after"/>
   </aop:aspect>
</aop:config>

测试:

public class MyTest {
   @Test
   public void test(){
       ApplicationContext context = newClassPathXmlApplicationContext("beans.xml");
       UserService userService = (UserService) context.getBean("userService");
       userService.add();
  }
}

9.2.3 使用注解实现

第一步:编写一个注解实现的增强类

package com.kuang.config;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AnnotationPointcut {
   @Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void before(){
       System.out.println("---------方法执行前---------");
  }

   @After("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void after(){
       System.out.println("---------方法执行后---------");
  }

   @Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void around(ProceedingJoinPoint jp) throws Throwable {
       System.out.println("环绕前");
       System.out.println("签名:"+jp.getSignature());
       //执行目标方法proceed
       Object proceed = jp.proceed();
       System.out.println("环绕后");
       System.out.println(proceed);
  }
}

第二步:在Spring配置文件中,注册bean,并增加支持注解的配置

<!--第三种方式:注解实现-->
<bean id="annotationPointcut" class="com.kuang.config.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/982238.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

解决Spring Boot中LocalDateTime返回前端数据为数组结构的问题

在Spring Boot开发中&#xff0c;处理日期时间数据是一个常见的需求。Java 8 引入了新的日期时间API&#xff0c;如LocalDateTime&#xff0c;它提供了更强大的日期时间处理功能。然而&#xff0c;在将LocalDateTime对象序列化为JSON时&#xff0c;可能会遇到返回为数组结构的问…

【一个月备战蓝桥算法】递归与递推

字典序 在刷题和计算机科学领域&#xff0c;字典序&#xff08;Lexicographical order&#xff09;也称为词典序、字典顺序、字母序&#xff0c;是一种对序列元素进行排序的方式&#xff0c;它模仿了字典中单词的排序规则。下面从不同的数据类型来详细解释字典序&#xff1a; …

CSDN 1024天 创作纪念日

机缘 还记得那是2022年5月&#xff0c;在上家公司工作时候&#xff0c;意外发现同事在通过CSDN记录一些日常遇到、解决的问题&#xff0c;也会更新一些他擅长领域的知识点&#xff0c;并且收获了不少的粉丝和阅读量&#xff0c;这不由得激起了我的兴趣。也在有空时候&#xff…

用于管理 Elasticsearch Serverless 项目的 AI Agent

作者&#xff1a;来自 Elastic Fram Souza 由自然语言驱动的 AI 代理&#xff0c;可轻松管理 Elasticsearch Serverless 项目 - 支持项目创建、删除和状态检查。 这个小型命令行工具让你可以用简单的英语管理你的无服务器 Elasticsearch 项目。它通过AI&#xff08;这里是 Ope…

机器学习数学通关指南

✨ 写在前面 &#x1f4a1; 在代码的世界里沉浸了十余载&#xff0c;我一直自诩逻辑思维敏捷&#xff0c;编程能力不俗。然而&#xff0c;当我初次接触 DeepSeek-R1 并领略其清晰、系统的思考过程时&#xff0c;我不禁为之震撼。那一刻&#xff0c;我深刻意识到&#xff1a;在A…

< 自用文儿 > DELETED 设置速读 in Ubuntu24

systemctl 和 DELETED&#xff1a; 配置文件&#xff1a; vi /etc/systemd/system/ DELETED.service [Unit] DescriptionV2Ray Service Documentation DELETED Afternetwork.target nss-lookup.target[Service] #Usernobody CapabilityBoundingSetCAP_NET_ADMIN CAP_NET_BIN…

intra-mart实现logicDesigner与forma联动

一、前言 有一个需求&#xff0c;想实现从页面上传一个excel文件&#xff0c;点击提交&#xff0c;就转发给forma模块&#xff0c;然后用户在forma模块里&#xff0c;确认下自动填写的信息是否正确&#xff0c;正确的话就点击保存&#xff0c;存入数据库&#xff1b;不正确的话…

优选算法的智慧之光:滑动窗口专题(二)

专栏&#xff1a;算法的魔法世界​​​​​​ 个人主页&#xff1a;手握风云 目录 一、例题讲解 1.1. 最大连续1的个数 III 1.2. 找到字符串中所有字母异位词 1.3. 串联所有单词的子串 1.4. 最小覆盖子串 一、例题讲解 1.1. 最大连续1的个数 III 题目要求是二进制数组&am…

Harbor端口更改||Harbor端口映射

Harbor端口更改|Harbor端口映射 目标&#xff1a;将端口更改为8930 前言 [rootk8s-node1 harbor]# ls common common.sh docker-compose.yml harbor.v2.5.0.tar.gz harbor.yml harbor.yml.tmpl install.sh LICENSE prepare如上是Harbor的文件目录 更改harbor.yml文件…

PGlite:浏览器中运行的PostgreSQL

PGlite 是一款基于 WebAssembly&#xff08;WASM&#xff09;构建的轻量级 PostgreSQL 数据库引擎&#xff0c;旨在简化开发者在浏览器、Node.js、Bun 或 Deno 环境中运行 PostgreSQL。PGlite 无需复杂的安装或配置&#xff0c;特别适合开发测试、本地化应用及快速原型设计。 一…

DeepSeek集成到VScode工具,让编程更高效

DeepSeek与VScode的强强联合&#xff0c;为编程效率树立了新标杆。 DeepSeek&#xff0c;一款卓越的代码搜索引擎&#xff0c;以其精准的索引和高速的检索能力&#xff0c;助力开发者在浩瀚的代码海洋中迅速定位关键信息。 集成至VScode后&#xff0c;开发者无需离开熟悉的编辑…

蓝桥杯 - 每日打卡(类斐波那契循环数)

题目: 解题思路&#xff1a; 假设输入数值为number 分析题目&#xff0c;如果想要解决这个问题&#xff0c;我们需要实现两个方法&#xff0c;第一个检查number是否是类斐波那契&#xff0c;第二个是模拟1e7 - 0的过程&#xff0c;因为是求最大的&#xff0c;那么我们从1e7开始…

JavaScript实现著名的“两数之和”问题

下面是使用 JavaScript 实现“两数之和”问题的一种常见解法&#xff0c;利用哈希表&#xff08;Map&#xff09;存储遍历过的数字和它们对应的下标&#xff0c;从而在一次遍历中完成查找。以下是详细的代码和说明&#xff1a; function twoSum(nums, target) {// 创建一个 Ma…

【微信小程序】每日心情笔记

个人团队的比赛项目&#xff0c;仅供学习交流使用 一、项目基本介绍 1. 项目简介 一款基于微信小程序的轻量化笔记工具&#xff0c;旨在帮助用户通过记录每日心情和事件&#xff0c;更好地管理情绪和生活。用户可以根据日期和心情分类&#xff08;如开心、平静、难过等&#…

【数据结构】什么是栈||栈的经典应用||分治递归||斐波那契问题和归并算法||递归实现||顺序栈和链栈的区分

文章目录 &#x1f967;栈的初步理解&#xff1a;&#x1f967;易错&#xff1a;如何判断栈满&#x1f967;栈满理解&#x1f967;栈的基本运算&#x1f4da;栈操作的伪代码逻辑&#xff08;顺序和链栈&#xff09;&#x1f4d5;顺序栈运算实现&#xff1a;顺序栈的表示&#x…

利用opencv_python(pdf2image、poppler)将pdf每页转为图片

1、安装依赖pdf2image pip install pdf2image 运行.py报错&#xff0c;因为缺少了poppler支持。 2、安装pdf2image的依赖poppler 以上命令直接报错。 改为手工下载&#xff1a; github: Releases oschwartz10612/poppler-windows GitHub 百度网盘&#xff1a; 百度网盘…

IDEA + DeepSeek 实现 AI辅助编程,提升效率10倍(全网超详细的终极图文实战指南)

前言 在软件开发的世界里&#xff0c;每个开发者都经历过这样的困境——在重复的CRUD代码中机械劳动&#xff0c;为复杂的业务逻辑调试数小时&#xff0c;或是在海量文档中寻找某个API的正确用法。传统的IDE工具虽能提供基础支持&#xff0c;却难以突破效率的“玻璃天花板”。而…

青训营:简易分布式爬虫

一、项目介绍 该项目是一个简易分布式爬虫系统&#xff0c;以分布式思想为基础&#xff0c;通过多节点协作的方式&#xff0c;将大规模的网页抓取任务分解&#xff0c;从而高效、快速地获取网络数据 。 项目地址&#xff1a;https://github.com/yanchengsi/distributed_crawle…

论坛系统测试报告

目录 一、项目背景二、论坛系统测试用例思维导图三、论坛系统测试3.1界面测试3.2登陆测试3.3主页测试3.4个人中心测试 四、自动化测试脚本4.1配置驱动4.2创建浏览器类4.3功能测试4.3.1登陆测试4.3.2注册测试4.3.3主页测试4.3.4帖子编辑4.3.5运行主代码 五、BUG分析六、测试总结…

C++ std::vector 超详细指南:基础实践(手搓vector)

目录 一.基本概念 二.类的常用接口说明 1.类对象的常见构造 2. vector类空间变化 1&#xff09;.size()&#xff08;获取数据个数&#xff09; 2&#xff09;.capacity()&#xff08;获取容量大小&#xff09; 3&#xff09;.empty()&#xff08;判断是否为空&#xff0…