您现在的位置是:首页 >其他 >Spring学习总结(二)网站首页其他

Spring学习总结(二)

路上阡陌 2024-07-21 06:01:03
简介Spring学习总结(二)

@Autowired 和 @Resource

@Autowired 和 @Resource 都是依赖注入(Dependency Injection,简称DI)的实现方式,都能够将容器中的Bean对象注入到其他对象中。

@Autowired是Spring框架提供的一种依赖注入方式,它通过按照类型(byType)自动注入容器中的Bean对象,同时支持按照名称(byName)进行注入。在使用@Autowired时需要注意,被注入的属性、方法或构造器参数类型必须与执行注入操作的Bean对象的类型一致或者与之相兼容。

代码示例:

public class OrderService {
    @Autowired
    private OrderDao orderDao;

    public void saveOrder(Order order) {
        orderDao.saveOrder(order);
    }
}

@Resource是Java EE规范中的注解,也可以实现依赖注入,它默认按照名称(byName)自动注入容器中的Bean对象,也可以通过指定name属性进行按照名称注入,还可以根据类型(byType)进行注入。在使用@Resource时需要注意,被注入的属性、方法或构造器参数名称必须与执行注入操作的Bean对象的名称一致。

代码示例:

public class OrderService {
    @Resource
    private OrderDao orderDao;

    public void saveOrder(Order order) {
        orderDao.saveOrder(order);
    }
}

两者的区别如下:

  1. 注入方式不同:@Autowired按照类型自动注入,@Resource默认按照名称自动注入。

  2. 对于同一个bean存在多个实例时,@Autowired无法指定具体注入哪个实例,而@Resource可以通过name属性指定要注入的实例。

  3. 兼容性:@Autowired只能在Spring框架中使用,而@Resource是Java EE规范中的注解,可以在Java EE容器中使用。

综上所述,两者的使用场景存在一定的差异。如果项目使用的框架是Spring,并且Bean对象的注入字段是私有的,则建议使用@Autowired;如果需要在不同的框架中使用,则建议使用@Resource。如果存在同名的多个Bean对象,建议使用@Resource,根据实际需要选择注入方式。

示例代码:

@Component("orderDao1")
public class OrderDaoImpl implements OrderDao {
    public void saveOrder(Order order) {
        // 实现保存订单的逻辑
    }
}

@Component("orderDao2")
public class OrderDaoImpl2 implements OrderDao {
    public void saveOrder(Order order) {
        // 实现保存订单的逻辑
    }
}

@Component
public class OrderService {
    @Autowired
    private OrderDao orderDao;

    @Resource(name="orderDao1")
    private OrderDao orderDaoRes1;

    public void saveOrder(Order order) {
        orderDao.saveOrder(order);
        orderDaoRes1.saveOrder(order);
    }
}

在上述示例代码中,我们使用@Autowired将容器中类型为OrderDao的Bean对象自动注入到OrderService中的orderDao属性中;使用@Resource将名称为orderDao1的Bean对象注入到OrderService中的orderDaoRes1属性中。在OrderService中调用了两个不同的Bean对象来保存订单数据。

Autowired 属于 Spring 内置的注解,默认的注入方式为byType(根据类型进行匹配) 这个类型指的是什么

@Autowired注解属于Spring内置的依赖注入注解,它的默认注入方式确实是根据类型进行匹配,即byType。所谓byType指的是Spring容器中的Bean类型,例如,我们在一个类中使用@Autowired注解引入某个Bean时,Spring会根据被依赖的类型来匹配容器中的Bean,并将匹配成功的Bean注入到目标对象中。

举个例子,假设我们有一个UserService接口和对应的UserServiceImpl实现类,另外还有一个类OrderService需要依赖UserService,我们可以通过@Autowired注解将UserService注入到OrderService中:

@Service
public class OrderService {
  @Autowired
  private UserService userService;

  // ...
}

在上面的代码中,@Autowired注解默认会根据类型(UserService)去容器中查找对应的Bean(UserServiceImpl),如果找到多个符合条件的Bean,则会抛出NoUniqueBeanDefinitionException异常,提示我们需要使用@Qualifier注解来明确指定注入哪个Bean。如果容器中没有对应的Bean,则会抛出NoSuchBeanDefinitionException异常。

需要注意的是,如果在容器中存在多个类型兼容的Bean,那么根据byType注入方式可能会存在无法确定注入哪个Bean的情况,有可能会产生歧义或不可预期的行为。因此,建议在这种情况下使用@Qualifier注解来明确注入哪个Bean,以便更精确地控制Bean的注入。

@Qualifier注解

@Qualifier是Spring框架提供的注解,用于配合@Autowired或@Resource注解一起使用,解决自动按照类型注入时可能产生的歧义问题。由于@Autowired和@Resource注解都是按照名称或类型进行自动注入,当一个接口或父类下有多个实现类或子类时,容器可能无法准确判断应该注入哪一个实现类或子类,@Qualifier注解可以解决这种歧义问题。

@Qualifier注解的属性值用于指示容器要注入哪一个具体的Bean对象,即对应的Bean对象的名称或标识符。通过指定@Qualifier注解的属性值,可以明确告诉容器注入哪一个实现类或子类。

示例代码:

public interface OrderDao {
    void saveOrder(Order order);
}

@Component("orderDaoImpl1")
public class OrderDaoImpl1 implements OrderDao {
    public void saveOrder(Order order) {
        System.out.println("使用OrderDaoImpl1保存订单数据");
    }
}

@Component("orderDaoImpl2")
public class OrderDaoImpl2 implements OrderDao {
    public void saveOrder(Order order) {
        System.out.println("使用OrderDaoImpl2保存订单数据");
    }
}

@Service
public class OrderService {
    @Autowired
    @Qualifier("orderDaoImpl1")
    private OrderDao orderDao;

    public void saveOrder(Order order) {
        orderDao.saveOrder(order);
    }
}

在上述代码中,我们定义了两个实现了OrderDao接口的Bean对象:OrderDaoImpl1和OrderDaoImpl2,它们都实现了saveOrder方法。然后在OrderService中通过@Autowired和@Qualifier注解注入orderDao属性,并指定要注入的是名称为"orderDaoImpl1"的Bean对象。

这样,在容器启动后,Spring将自动检测到OrderService中的orderDao属性需要进行注入,然后根据@Qualifier注解指定的名称找到orderDaoImpl1 Bean对象,最终将其注入到orderDao属性中。

需要注意的是,在使用@Qualifier注解时,指定的属性值必须要是对应Bean对象的标识符或名称,否则会抛出NoSuchBeanDefinitionException异常。

Spring AOP

Spring AOP(面向切面编程)是Spring框架中重要的组成部分之一,它提供了一种简单的方法来实现横切关注点的功能,例如事务管理、安全检查、日志处理等,这些功能通常是与业务逻辑分离的。AOP通过在运行时动态地将捕获的方法和函数组织成切面,然后根据需要在程序的适当位置调用,以增加程序的可维护性和可扩展性。

在Spring AOP实现中,像是前置增强、后置增强、环绕增强、异常通知这些概念都是定义成Advice的实现类,然后将这些Advice组合起来,形成一个切面,应用到需要增强的目标类上。

Spring AOP提供了两种类型的AOP代理:JDK动态代理和CGLib代理。JDK动态代理是通过反射机制在运行时创建一个目标类的接口实现类来完成代理对象的创建和管理,而CGLib代理是通过继承目标类的方式,动态生成目标类的子类,并重写目标类中需要增强的方法,从而完成代理对象的创建和管理。

在使用Spring AOP时,需要通过配置文件或注解的方式定义切面、通知和切点,然后通过Spring IoC容器来创建切面并将其应用到目标类上。

示例代码(基于XML配置方式):

<!-- 定义目标对象 -->
<bean id="targetObject" class="com.example.demo.service.UserServiceImpl" />

<!-- 定义切面对象 -->
<bean id="myAspect" class="com.example.demo.aop.MyAspect" />

<!-- 配置AOP -->
<aop:config>
    <!-- 配置切点 -->
    <aop:pointcut id="myPointcut"
        expression="execution(* com.example..*.*(..))" />

    <!-- 配置增强通知 -->
    <aop:advisor advice-ref="beforeAdvice" pointcut-ref="myPointcut" />
    <aop:advisor advice-ref="afterAdvice" pointcut-ref="myPointcut" />
    <aop:advisor advice-ref="aroundAdvice" pointcut-ref="myPointcut" />
    <aop:advisor advice-ref="afterThrowingAdvice" pointcut-ref="myPointcut" />

    <!-- 定义增强通知的实现 -->
    <bean id="beforeAdvice" class="com.example.demo.aop.BeforeAdvice" />
    <bean id="afterAdvice" class="com.example.demo.aop.AfterAdvice" />
    <bean id="aroundAdvice" class="com.example.demo.aop.AroundAdvice" />
    <bean id="afterThrowingAdvice" class="com.example.demo.aop.AfterThrowingAdvice" />
</aop:config>

在上述代码中,我们通过定义targetObject和四个不同类型的增强通知(beforeAdvice、afterAdvice、aroundAdvice、afterThrowingAdvice),并将其引用到myPointcut切点上,创建了一个AOP代理,并将其配置在Spring的AOP配置文件中。

需要注意的是,上述示例使用了基于XML的方式进行配置,除此之外,Spring AOP还支持基于注解的配置方式。对于一些简单的AOP需求,可以直接使用基于注解的方式,这样可以避免在XML配置文件中定义切面、切点和增强通知等内容。

JDK动态代理是通过反射机制在运行时创建一个目标类的接口实现类来完成代理对象的创建和管理

Java中的动态代理,指的是在运行时动态地创建代理实例的代理方式。JDK动态代理是Java动态代理机制的一种实现方式。JDK动态代理是通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现的。

以下是一个简易的例子,模拟了一个简单的支付过程,使用JDK动态代理实现对支付过程的切面增强,将支付过程记录到日志中。

首先定义一个支付接口和实现类,如下:

public interface Payment {
    void pay(double amount);
}
public class PaymentImpl implements Payment {
    public void pay(double amount) {
        System.out.println("支付 " + amount + " 元");
    }
}

接着定义一个InvocationHandler接口的实现类,实现invoke()方法,并在该方法中实现对支付过程的增强处理(记录日志等),示例代码如下:

public class PaymentProxy implements InvocationHandler {
    private Payment target;

    public PaymentProxy(Payment target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用方法: " + method.getName());
        System.out.println("参数列表: " + Arrays.toString(args));
        Object result = method.invoke(target, args);
        System.out.println("支付完成");
        return result;
    }
}

在该类中,我们传入一个Payment类型的目标对象,并在invoke()方法中实现了增强处理。

最后,在测试类中调用PaymentProxy创建代理对象并调用其中的方法,示例代码如下:

public class Main {
    public static void main(String[] args) {
        Payment target = new PaymentImpl();
        Payment payment = (Payment) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new PaymentProxy(target)
        );
        payment.pay(100.0);
    }
}

在该类中,我们首先创建了Payment类型的目标对象PaymentImpl,然后通过调用Proxy类的newProxyInstance()方法创建了一个支付代理对象Payment,该代理对象在调用pay()方法时,会自动执行PaymentProxy中实现的增强处理代码。

这样,我们就可以通过JDK动态代理机制,实现对支付过程的切面增强。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。