Spring系列二:基于注解配置bean【建议收藏】

news/2024/5/2 9:13:35/文章来源:https://blog.csdn.net/qq_18817831/article/details/131897884

文章目录

  • 💗通过注解配置bean
    • 🍝基本介绍
    • 🍝快速入门
    • 🍝注意事项和细节
  • 💗自己实现Spring注解配置Bean机制
    • 🍝需求说明
    • 🍝思路分析
    • 🍝注意事项和细节
  • 💗自动装配 @Autowired
    • 🍝`案例1:` @Autowired引出
    • 🍝`案例2:` @Autowired解读
    • 🍚`案例3:` @Resource解读
    • 🍝小结
  • 💗泛型依赖注入

上文中, 我们学习到了 Spring系列一:spring的安装与使用

接下来我们学习, 通过注解配置bean
在这里插入图片描述

💗通过注解配置bean

🍝基本介绍

基于注解的方式配置bean, 主要是项目开发中的组件, 比如Controller, ServiceDao.

  • 组件注解的形式有
    1.@Component 表示当前注解标识的是一个组件
    2.@Controller 表示当前注解标识的是一个控制器, 通常用于Servlet
    3.@Service 表示当前注解表示的是一个处理业务逻辑的类, 通常用于Service
    4.@Repository表示当前注解标识的是一个持久化的类, 通常用于Dao

🍝快速入门

  • 应用实例
    使用注解的方式来配置 Controller / Service / Repository / Component

  • 代码实现
    1引入spring-aop-5.3.8.jar, 在 spring/lib 目录下拷贝即可
    2.在spring/component包下 创建 UserAction .java, UserService.java, UserDao.java, MyComponent.java

//使用 @Repository 标识该类是一个Repository, 即是一个持久化层的类/对象
@Repository
public class UserDao {}
//@Service 标识该类是一个Service类/对象
@Service
public class UserService {}
//@Controller 标识该类是一个控制器Controller, 通常该类是一个Servlet
@Controller
public class UserAction {}
//@Component 标识该类是一个组件, 是一个通用的注解
@Component
public class MyComponent {}

配置文件
在这里插入图片描述
在这里插入图片描述

beans05.xml

<!--配置容器要扫描的包
解读:
1.component-scan 要对指定包下的类进行扫描, 并创建对象到我们的容器中
2.base-package 指定要扫描的包
3.含义是当spring容器创建/初始化时, 就会扫描com.zzw.spring.component包下的所有的 有注解 @Controller / @Service / @Repository / @Component 的类将其实例化, 生成对象, 放入到ioc容器
-->
<context:component-scan base-package="com.zzw.spring.component"/>

通过注解来配置bean

public class SpringBeanTest {//通过注解来配置bean@Testpublic void setBeanByAnnotation() {ApplicationContext ioc =new ClassPathXmlApplicationContext("beans05.xml");UserAction userAction = ioc.getBean(UserAction.class);UserService userService = ioc.getBean(UserService.class);UserDao userDao = ioc.getBean(UserDao.class);MyComponent myComponent = ioc.getBean(MyComponent.class);System.out.println("userDao=" + userDao);System.out.println("userService=" + userService);System.out.println("userAction=" + userAction);System.out.println("myComponent=" + myComponent);System.out.println("ok");}
}

🍝注意事项和细节

1.需要导入spring-aop-5.3.8.jar, 别忘了
2.必须在Spring配置文件中指定 “自动扫描的包”, IOC容器才能够检测到当前项目中哪些类被标识了注解, 注意必须导入context名称空间

<!–配置自动扫描的包–>
<context:component-scan base-package="com.zzw.spring.component"/>
可以使用通配符 * 来指定, 比如 com.zzw.spring.* 表示

问题: com.zzw.spring.component 会不会去扫描它的子包?

3.Spring的IOC容器不能检测一个使用了@Controller注解的类到底是不是一个真正的控制器. 注解的名称是用于程序员自己识别当前标识的是什么组件. 其它的@Service, @Repository也是一样的道理. [也就是说spring的IOC容器只要检查到注解就会生成对象, 但是这个注解的含义spring不会识别, 注解是给程序员编程方便看的.]

4.<context:component-scan base-package=“com.zzw.spring.component” resource-pattern=“User*.class”/>
注意1: resource-pattern=“User*.class”: 表示只扫描 com.zzw.spring.component包 和 它的子包下的User打头的类.[使用的少, 不想扫描, 不写注解就可以]
注意2: 真正运行的是out目录, 所以扫描的是.class文件
在这里插入图片描述
5.排除一些类, 以annotation注解为例
<context: exclude-filter type=“annotation” expression=“org.springframework.stereotype.Service”/>

<!--需求: 如果我们希望排除某个包/子包下的某种类型的注解, 可以通过exclude-filter来指定1.context:exclude-filter 指定要排除哪些类2.type 指定排除方式 annotation表示按照注解来排除3.expression="org.springframework.stereotype.Service" 指定要排除的注解的全路径
-->
<context:component-scan base-package="com.zzw.spring.component"><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>

– 通过Debug IOC容器结构, 可以一目了然.
在这里插入图片描述
解读
1)<context:exclude-filter> 放在 <context:component-scan>内, 表示扫描需要过滤掉当前包及其子包的某些类
2)type=“annotation”* 按照注解类型进行过滤
3)expression: 就是注解的全类名, 比如org.springframework.stereotype.Service就是@Service注解的全类名, 其他比如@Controller @Repository等, 依此类推
4)上面表示过滤掉com.zzw.spring.component包及其子包下, 加入了@Service 注解的类
5)测试, 修改beans05.xml, 增加exclude-filter, 发现UserService, 不会注入到容器.

6.指定自动扫描哪些注解类

<!--需求: 如果我们希望按照自己的规则, 来扫描包/子包下的某些注解, 可以通过include-filter来指定1.use-default-filters="false" 表示不使用默认的 扫描机制/过滤机制2.context:include-filter 表示要去扫描哪些类3.type="annotation" 按照注解的方式去 扫描/过滤4.expression="org.springframework.stereotype.Service" 指定要扫描的注解的全路径
-->
<context:component-scan base-package="com.zzw.spring.component" use-default-filters="false"><context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

7.默认情况: 标记注解后, 类名首字母小写作为id的值, 也可以使用注解的value属性指定id值, 并且value可以省略.

//在默认情况下, 注解标识的类创建对象后, 在容器中, id 为类名的首字母小写
UserDao userDao1 = ioc.getBean("userDao", UserDao.class);
System.out.println("userDao1=" + userDao1);
 /** * 解读* 1.标记注解后, 类名首字母小写作为id的值(默认)* 2.value="zzwUserDao" 使用指定的 zzwUserDao作为UserDao对象的id*/
@Repository(value = "zzwUserDao")
public class UserDao {}

在这里插入图片描述
8.关于@Controller, @Service, @Component区别:
https://zhuanlan.zhihu.com/p/454638478

💗自己实现Spring注解配置Bean机制

🍝需求说明

1.自己写一个简单的Spring容器, 通过读取类的注解(@Component, @Controller, @Service, @Repository), 将对象注入到IOC容器
2.也就是说, 不使用Spring原生框架, 我们自己使用 IO + Annotation + 反射 + 集合 技术实现, 打通Spring注解方式开发的技术痛点.

IO知识,传送门 - 注解知识,传送门 - 反射知识,传送门 - 集合知识,传送门

🍝思路分析

1)思路分析+程序结构
2)我们使用注解方式完成, 这里我们不使用xml来配置
3)程序框架图
在这里插入图片描述

1.搭建基本结构并获取扫描的包

自定义注解 ComponentScan

/*** 1. @Target(ElementType.TYPE)指定我们的ComponentScan注解可以修饰 Type程序元素* 2. @Retention(RetentionPolicy.RUNTIME)指定ComponentScan注解 保留范围* 3. String value() default "": 表示ComponentScan注解可以传入 value*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {String value() default "";
}

容器配置文件 ZzwSpringConfig

//这是一个配置类, 它的作用类似于我们原生spring的 beans.xml 容器配置文件
@ComponentScan(value = "com.zzw.spring.component")
public class ZzwSpringConfig {}

模拟Spring-ioc容器 ZzwSpringApplicationContext

//ZzwSpringApplicationContext 类的作用类似Spring原生ioc容器
@SuppressWarnings({"all"})
public class ZzwSpringApplicationContext {private Class configClass;//ioc存放的就是通过反射创建的对象(基于注解方式)private final ConcurrentHashMap<String, Object> ioc =new ConcurrentHashMap<>();//构造器public ZzwSpringApplicationContext(Class configClass) {this.configClass = configClass;//System.out.println("this.configClass=" + this.configClass);//获取要扫描的包//1.先得到ZzwSpringConfig配置类的 @ComponentScan(value = "com.zzw.spring.component")ComponentScan componentScan =(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);//2.获取componentScan的value => 即要扫描的包String path = componentScan.value();System.out.println("要扫描的包=" + path);}
}

测试

public class ZzwSpringApplicationContextTest {public static void main(String[] args) {ZzwSpringApplicationContext ioc =new ZzwSpringApplicationContext(ZzwSpringConfig.class);}
}

2.获取扫描包下所有的.class文件 [扫描的out目录下的component目录, 里面都是.class后缀的文件]

在这里插入图片描述

public class ZzwSpringApplicationContext {private Class configClass;//ioc存放的就是通过反射创建的对象(基于注解方式)private final ConcurrentHashMap<String, Object> ioc =new ConcurrentHashMap<>();//构造器public ZzwSpringApplicationContext(Class configClass) {this.configClass = configClass;//System.out.println("this.configClass=" + this.configClass);//获取要扫描的包//1.先得到ZzwSpringConfig配置类的 @ComponentScan(value = "com.zzw.spring.component")ComponentScan componentScan =(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);//2.通过componentScan的value => 即要扫描的包String path = componentScan.value();System.out.println("要扫描的包=" + path);//com.zzw.spring.component//得到要扫描的包下的所有资源(.class 类)//1.得到类的加载器ClassLoader classLoader = ZzwApplicationContext.class.getClassLoader();//2.通过类的加载器获取到要扫描包的资源url =>类似一个路径path = path.replace(".", "/");//一定要把 .替换成 /URL resource = classLoader.getResource(path);//file:/D:/idea_project/zzw_spring/spring/out/production/spring/com/zzw/spring/componentSystem.out.println("resource=" + resource);//3.将要加载的资源(.class) 路径下的文件进行遍历File file = new File(resource.getFile());//在io中, 目录也是文件if (file.isDirectory()) {//检查是否是目录File[] files = file.listFiles();for (File f : files) {System.out.println("=============================");System.out.println(f.getAbsolutePath());}}}
}

测试

public class ZzwSpringApplicationContextTest {public static void main(String[] args) {ZzwSpringApplicationContext ioc =new ZzwSpringApplicationContext(ZzwSpringConfig.class);}
}

3.获取全类名 反射对象 放入容器

public class ZzwSpringApplicationContext {private Class configClass;//ioc存放的就是通过反射创建的对象(基于注解方式)private final ConcurrentHashMap<String, Object> ioc =new ConcurrentHashMap<>();//构造器public ZzwSpringApplicationContext(Class configClass) {this.configClass = configClass;System.out.println("this.configClass=" + this.configClass);//获取要扫描的包//1.先得到ZzwSpringConfig配置类的 @ComponentScan(value = "com.zzw.spring.component")ComponentScan componentScan =(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);//2.通过componentScan的value => 即要扫描的包String path = componentScan.value();System.out.println("要扫描的包=" + path);//com.zzw.spring.component//得到要扫描的包下的所有资源(.class 类)//1.得到类的加载器ClassLoader classLoader = ZzwApplicationContext.class.getClassLoader();//2.通过类的加载器获取到要扫描包的资源url =>类似一个路径path = path.replace(".", "/");//一定要把 .替换成 / com/zzw/spring/componentURL resource = classLoader.getResource(path);//file:/D:/idea_project/zzw_spring/spring/out/production/spring/com/zzw/spring/componentSystem.out.println("resource=" + resource);//3.将要加载的资源(.class) 路径下的文件进行遍历File file = new File(resource.getFile());//在io中, 目录也是文件if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {System.out.println("=============================");System.out.println(f.getAbsolutePath());//fileAbsolutePath: D:\idea_project\zzw_spring\spring\out\production\spring\com\zzw\spring\component\UserDao.class//获取到 com.zzw.spring.component.UserDaoString fileAbsolutePath = f.getAbsolutePath();//这里我们只处理.class文件if (fileAbsolutePath.endsWith(".class")) {//1.获取类名String className =fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.lastIndexOf(".class"));System.out.println("className="+className);//2.获取类的完整的路径(全类名)// path.replace("/", ".") => com.zzw.spring.componentString classFullName = path.replace("/", ".") + "." + className;//比如 com.zzw.spring.component.UserDao//System.out.println("classFullName=" + classFullName);//3.判断该类是不是需要注入到容器, 就看该类是不是有注解 @Component @Controller...try {//这时, 我们就得到了该类的Class对象//Class clazz = Class.forName(classFullName)//说明//1. Class clazz = Class.forName(classFullName) 可以反射加载类//2. classLoader.loadClass(classFullName); 可以反射类的Class//3. 区别是: 上面方式会调用该类的静态方法, 下面方式不会//4. aClass.isAnnotationPresent(Component.class) 判断该类是否有 Component注解Class<?> aClass = classLoader.loadClass(classFullName);if (aClass.isAnnotationPresent(Component.class) ||aClass.isAnnotationPresent(Repository.class) ||aClass.isAnnotationPresent(Service.class) ||aClass.isAnnotationPresent(Controller.class)) {//这时就可以反射对象, 并放入到容器中Class<?> clazz = Class.forName(classFullName);Object instance = clazz.newInstance();//放入到容器中, 将类名的首字母小写作为id//StringUtils 工具类 import org.springframework.util.StringUtils;ioc.put(StringUtils.uncapitalize(className), instance);}} catch (Exception e) {throw new RuntimeException(e);}}}}}//编写方法返回容器对象public Object getBean(String name) {return ioc.get(name);}
}

测试

public class ZzwSpringApplicationContextTest {public static void main(String[] args) {ZzwSpringApplicationContext ioc =new ZzwSpringApplicationContext(ZzwSpringConfig.class);UserAction userAction = (UserAction) ioc.getBean("userAction");UserDao userDao = (UserDao) ioc.getBean("userDao");UserService userService = (UserService) ioc.getBean("userService");MyComponent myComponent = (MyComponent) ioc.getBean("myComponent");System.out.println("userAction=" + userAction);System.out.println("userDao=" + userDao);System.out.println("userService=" + userService);System.out.println("myComponent=" + myComponent);System.out.println("ok");}
}

🍝注意事项和细节

案例:还可以通过 @Component(value = “xx”) @Controller(value = “yy”) @Service(value = “zz”) 中指定的 value, 给bean分配id

@Component(value = "zzw1")
public class MyComponent {}
if (aClass.isAnnotationPresent(Component.class) ||aClass.isAnnotationPresent(Repository.class) ||aClass.isAnnotationPresent(Service.class) ||aClass.isAnnotationPresent(Controller.class)) {//这里我们演示一个Component注解指定value, 分配id//这里就是演示了一下机制if (aClass.isAnnotationPresent(Component.class)) {//获取到该注解Component component = aClass.getDeclaredAnnotation(Component.class);//获取component的value =>即要分配的idString id = component.value();if (!"".equals(id)) {className = id;}}//这时就可以反射对象, 并放入到容器中Class<?> clazz = Class.forName(classFullName);Object instance = clazz.newInstance();//放入到容器中, 将类名的首字母小写作为id//StringUtils 工具类ioc.put(StringUtils.uncapitalize(className), instance);
}

测试结果
在这里插入图片描述

💗自动装配 @Autowired

基本说明
1.基于注解配置bean, 也可实现自动装配. 使用的注解是: @Autowired 或者 @Resource

  • @Autowired的规则说明
    1. 在ioc容器中查找待装配的组件的类型, 如果有唯一的bean则匹配.

    2. 如果装配的类型对应的bean在ioc容器中有多个, 则使用待装配的属性的属性名作为id值再进行查找, 找到就装配, 找不到就抛异常.

  • @Resource的规则说明
    1. @Resource有两个属性是有两个属性是比较重要的, 分别是name和type. Spring将@Resource注解的name属性解析为bean的名字, 将type属性解析为bean的类型.

    2. 如果使用name属性, 则使用byName的自动注入策略; 而使用type属性时, 则使用byType自动注入策略.

    3. 如果@Resource 没有指定 name 和 type, 则优先使用byName注入策略 (即使用待装配的属性的属性名作为id值进行查找), 如果匹配不上, 再使用byType策略(即查找待装配的组件的类型). 如果都不成功, 就会报错.

  1. 不管是@Autowired 还是 @Resource 都保证属性名是规范写法就可以 注入.

🍝案例1: @Autowired引出

@Service
public class UserService {public void hi() {System.out.println("UserService hi()...");}
}
@Controller
public class UserAction {private UserService userService;public void sayOK() {System.out.println("UserAction sayOK()....");userService.hi();}
}
<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.zzw.spring.component"/>
</beans>
public class SpringBeanTest {@Testpublic void setPropertyByAutowired() {ApplicationContext ioc =new ClassPathXmlApplicationContext("beans06.xml");UserAction userAction = ioc.getBean("userAction", UserAction.class);System.out.println("userAction=" + userAction);//这里会输出userAction.sayOK();//这里会报空指针异常}
}

加入@Autowired, 就不会报错了.

@Controller
public class UserAction {@Autowiredprivate UserService userService;public void sayOK() {System.out.println("UserAction sayOK()....");userService.hi();}
}

🍝案例2: @Autowired解读

下面的代码中, UserAction中的userService200 和 SpringBeanTest中的userService是同一个对象. 因为ioc容器中只有一个UserService类型的对象, 此时按照类型来匹配.

@Service
public class UserService {public void hi() {System.out.println("UserService hi()...");}
}
@Controller
public class UserAction {//原先的xml配置, 我们会配置ref. 但是注解配置的情况下, 我们会用@Autowired//说明//1.在ioc容器中查找待装配的组件的类型, 如果有唯一的bean匹配(按照类型), 则使用该bean匹配//2.如果待装配的类型对应的bean在ioc容器中有多个, 则使用待装配的属性的属性名作为id值再进行查找//  找到就装配, 找不到就抛异常@Autowiredprivate UserService userService200;public void sayOK() {System.out.println("UserAction sayOK()....");System.out.println("UserAction 装配的 userService属性=" + userService200);userService200.hi();}
}
<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!--通过注解获取的对象id是类名首字母小写--><context:component-scan base-package="com.zzw.spring.component"/>
</beans>
public class SpringBeanTest {@Testpublic void setPropertyByAutowired() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans06.xml");UserService userService = ioc.getBean("userService", UserService.class);System.out.println("ioc容器中的userService=" + userService);userAction.sayOK();}
}

如果在beans06.xml中加入了同一类型(UserService)的对象, 如下. 那么UserAction中的userService200 和 SpringBeanTest中的userService将不再是同一个对象. 因为ioc容器中UserService类型的对象有多个, 此时将按照待匹配属性的属性名作为id值来匹配, 匹配到的是id为userService200的对象.

<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!--通过注解获取的对象id是类名首字母小写--><context:component-scan base-package="com.zzw.spring.component"/><!--id=userService--><!--配置两个UserService对象--><bean class="com.zzw.spring.component.UserService" id="userService200"/><bean class="com.zzw.spring.component.UserService" id="userService300"/>
</beans>

如果UserAction改为如下情况, 那么将会报错. 因为此时是按照待匹配属性的属性名作为id值来匹配的, 但beans06.xml中并没有id=userService400的bean对象, 所以报错.

@Controller
public class UserAction {//原先的xml配置, 我们会配置ref. 但是注解配置的情况下, 我们会用@Autowired//说明//1.在ioc容器中查找待装配的组件的类型, 如果有唯一的bean匹配(按照类型), 则使用该bean匹配//2.如果待装配的类型对应的bean在ioc容器中有多个, 则使用待装配的属性的属性名作为id值再进行查找//  找到就装配, 找不到就抛异常@Autowiredprivate UserService userService400;public void sayOK() {System.out.println("UserAction sayOK()....");System.out.println("UserAction 装配的 userService属性=" + userService400);userService400.hi();}
}

指定id进行组装. 可以使用@Autowired和@Qualifier(value=“userService200”). 这时, 是装配的 id=userService200, 类似于@Resource(name="userService200"), 两个注解要配合使用.
此时, UserAction中的userService 和 SpringBeanTest中的userService200是同一个对象.

@Controller
public class UserAction {//指定id进行组装. 可以使用@Autowired和@Qualifier(value="userService200"). //这时, 是装配的 id=userService200, 类似于@Resource(name="userService200").//前提是需要两个注解都写上. @Autowired@Qualifier(value = "userService200")private UserService userService;public void sayOK() {System.out.println("UserAction sayOK()....");System.out.println("UserAction 装配的 userService属性=" + userService);userService.hi();}
}

🍚案例3: @Resource解读

@Resource源码解读: 通过解析注解来支撑, 底层是注解来支撑的.
String name() default "";
Class<?> type() default java.lang.Object.class;

@Resource(name = "userService") 代表把beans06.xml对应的容器里的id=userService的对象注入到属性上去, 所以UserAction中的userService400 和 SpringBeanTest中的userService是同一个对象

@Controller
public class UserAction {//1.@Resource 有两个属性是比较重要的, 分别是name和type, Spring将@Resource注解的name属性解析为bean的名字,//  将type属性解析为bean的类型. 所以如果使用name属性, 则使用byName的自动注入策略, 而使用type属性时则使用//  byType自动注入策略//  比如 @Resource(name="userService") 表示装配id=userService的对象//  比如 @Resource(type="UserService.class") 表示按照UserService.class类型进行装配. 这时要求容器中,只能有一个这样类型的对象//2.如果@Resource 没有指定 name 和 type, 则先使用byName注入策略 (即使用待装配的属性的属性名作为id值进行查找),//  如果匹配不上, 再使用byType策略. 如果都不成功, 就会报错.@Resource(name = "userService")private UserService userService400;public void sayOK() {System.out.println("UserAction sayOK()....");System.out.println("UserAction 装配的 userService属性=" + userService400);userService400.hi();}
}
<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!--通过注解获取的对象id是类名首字母小写, 这里是userService--><context:component-scan base-package="com.zzw.spring.component"/><!--id=userService--><!--配置两个UserService对象--><bean class="com.zzw.spring.component.UserService" id="userService200"/><bean class="com.zzw.spring.component.UserService" id="userService300"/>
</beans>
public class SpringBeanTest {@Testpublic void setPropertyByAutowired() {ApplicationContext ioc =new ClassPathXmlApplicationContext("beans06.xml");UserService userService = ioc.getBean("userService", UserService.class);System.out.println("ioc容器中的userService=" + userService);UserAction userAction = ioc.getBean("userAction", UserAction.class);userAction.sayOK();}
}

如果将代码改为@Resource(name = "userService200") 代表把beans06.xml对应的容器里的id=userService200的对象注入到属性上去, 那么UserAction中的userService400 和 SpringBeanTest中的userService200将是同一个对象,

@Controller
public class UserAction {//1.@Resource 有两个属性是比较重要的, 分别是name和type, Spring将@Resource注解的name属性解析为bean的名字,//  将type属性解析为bean的类型. 所以如果使用name属性, 则使用byName的自动注入策略, 而使用type属性时则使用//  byType自动注入策略//  比如 @Resource(name="userService") 表示装配id=userService的对象@Resource(name = "userService200")private UserService userService400;public void sayOK() {System.out.println("UserAction sayOK()....");System.out.println("UserAction 装配的 userService属性=" + userService400);userService400.hi();}
}
<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!--通过注解获取的对象id是类名首字母小写, 这里是userService--><context:component-scan base-package="com.zzw.spring.component"/><!--id=userService--><!--配置两个UserService对象--><bean class="com.zzw.spring.component.UserService" id="userService200"/><bean class="com.zzw.spring.component.UserService" id="userService300"/>
</beans>
public class SpringBeanTest {@Testpublic void setPropertyByAutowired() {ApplicationContext ioc =new ClassPathXmlApplicationContext("beans06.xml");UserService userService = ioc.getBean("userService", UserService.class);System.out.println("ioc容器中的userService=" + userService);UserService userService200 = ioc.getBean("userService200", UserService.class);System.out.println("ioc容器中的userService200=" + userService200);UserAction userAction = ioc.getBean("userAction", UserAction.class);userAction.sayOK();}
}

如果将代码改为@Resource(name = "userService600") 会直接报错. 因为beans06.xml对应的容器中没有id为userService600的对象, 所以报错.

@Controller
public class UserAction {//1.@Resource 有两个属性是比较重要的, 分别是name和type, Spring将@Resource注解的name属性解析为bean的名字,//  将type属性解析为bean的类型. 所以如果使用name属性, 则使用byName的自动注入策略, 而使用type属性时则使用//  byType自动注入策略//  比如 @Resource(name="userService") 表示装配id=userService的对象@Resource(name = "userService600")private UserService userService400;public void sayOK() {System.out.println("UserAction sayOK()....");System.out.println("UserAction 装配的 userService属性=" + userService400);userService400.hi();}
}
<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!--通过注解获取的对象id是类名首字母小写, 这里是userService--><context:component-scan base-package="com.zzw.spring.component"/><!--id=userService--><!--配置两个UserService对象--><bean class="com.zzw.spring.component.UserService" id="userService200"/><bean class="com.zzw.spring.component.UserService" id="userService300"/>
</beans>

如果按照类型来装配, @Resource(type = UserService.class), 那么必须保证容器中该类型的对象只有一个.
像下面的情况就会报错.

public class UserAction {@Resource(type = UserService.class)private UserService userService400;public void sayOK() {System.out.println("UserAction sayOK()....");System.out.println("UserAction 装配的 userService属性=" + userService400);userService400.hi();}
}
<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!--通过注解获取的对象id是类名首字母小写--><context:component-scan base-package="com.zzw.spring.component"/><!--id=userService--><!--配置两个UserService对象--><bean class="com.zzw.spring.component.UserService" id="userService200"/><bean class="com.zzw.spring.component.UserService" id="userService300"/>
</beans>

如果@Resource 没有指定 name 和 type, 则先使用byName注入策略 (即使用待装配的属性的属性名作为id值进行查找),如果匹配不上, 再使用byType策略. 如果都不成功, 就会报错. 像下面的代码就会报错.

public class UserAction {//2.如果@Resource 没有指定 name 和 type, 则先使用byName注入策略 (即使用待装配的属性的属性名作为id值进行查找),//  如果匹配不上, 再使用byType策略. 如果都不成功, 就会报错.@Resourceprivate UserService userService400;public void sayOK() {System.out.println("UserAction sayOK()....");System.out.println("UserAction 装配的 userService属性=" + userService400);userService400.hi();}
}
<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!--通过注解获取的对象id是类名首字母小写--><context:component-scan base-package="com.zzw.spring.component"/><!--id=userService--><!--配置两个UserService对象--><bean class="com.zzw.spring.component.UserService" id="userService200"/><bean class="com.zzw.spring.component.UserService" id="userService300"/>
</beans>

下面代码会成功

public class UserAction {//2.如果@Resource 没有指定 name 和 type, 则先使用byName注入策略 (即使用待装配的属性的属性名作为id值进行查找),//  如果匹配不上, 再使用byType策略. 如果都不成功, 就会报错.@Resourceprivate UserService userService;public void sayOK() {System.out.println("UserAction sayOK()....");System.out.println("UserAction 装配的 userService属性=" + userService);userService.hi();}
}

下面代码依然会成功

public class UserAction {//2.如果@Resource 没有指定 name 和 type, 则先使用byName注入策略 (即使用待装配的属性的属性名作为id值进行查找),//  如果匹配不上, 再使用byType策略. 如果都不成功, 就会报错.@Resourceprivate UserService userService200;public void sayOK() {System.out.println("UserAction sayOK()....");System.out.println("UserAction 装配的 userService属性=" + userService200);userService200.hi();}
}

下面代码会失败. 因为beans.xml中没有id=userServise600的bean对象, byName注入策略不成功, 并且byType注入策略也不成功, 所以会失败.

public class UserAction {//2.如果@Resource 没有指定 name 和 type, 则先使用byName注入策略 (即使用待装配的属性的属性名作为id值进行查找),//  如果匹配不上, 再使用byType策略. 如果都不成功, 就会报错.@Resourceprivate UserService userService600;public void sayOK() {System.out.println("UserAction sayOK()....");System.out.println("UserAction 装配的 userService属性=" + userService600);userService600.hi();}
}

但是下面的代码会成功. 虽然beans.xml中没有id=userServise600的bean对象, 即byName注入策略不成功, 但是由于beans06.xml对应的容器中只有一个UserService类型的对象, 所以byType策略成功, 所以下面代码不会报错.

public class UserAction {//2.如果@Resource 没有指定 name 和 type, 则先使用byName注入策略 (即使用待装配的属性的属性名作为id值进行查找),//  如果匹配不上, 再使用byType策略. 如果都不成功, 就会报错.@Resourceprivate UserService userService600;public void sayOK() {System.out.println("UserAction sayOK()....");System.out.println("UserAction 装配的 userService属性=" + userService600);userService600.hi();}
}
<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!--通过注解获取的对象id是类名首字母小写--><context:component-scan base-package="com.zzw.spring.component"/><!--id=userService--><!--配置两个UserService对象--><!--<bean class="com.zzw.spring.component.UserService" id="userService200"/>--><!--<bean class="com.zzw.spring.component.UserService" id="userService300"/>-->
</beans>

🍝小结

1.如果装配的类型对应的bean在IOC容器中有多个, 则使用待装配的属性的属性名作为id值再进行查找, 找到就装配, 找不到就抛异常.

💗泛型依赖注入

基本说明
1.为了更好地管理有继承和相互依赖的bean的自动装配, spring还提供基于泛型依赖的注入机制.
2.在继承关系复杂情况下, 泛型依赖注入就会有很大的优越性.

各个类关系图
在这里插入图片描述


传统方法是将 PhoneDao / BookDao 自动装配到 BookService / PhoneService中, 当这种继承关系多时, 就比较麻烦. 可以使用spring提供的泛型依赖注入.

public class Book {}
public class Phone {}
//自定义泛型类
public abstract class BaseDao<T> {public abstract void save();
}
@Repository
public class BookDao extends BaseDao<Book> {@Overridepublic void save() {System.out.println("BookDao的 save方法....");}
}
@Repository
public class PhoneDao extends BaseDao<Phone> {@Overridepublic void save() {System.out.println("PhoneDao的 save方法...");}
}
//自定义泛型类
public class BaseService<T> {@Autowiredprivate BaseDao<T> baseDao;public void save() {baseDao.save();}
}
@Service
public class BookService extends BaseService<Book> {//并没有写属性
}
@Service
public class PhoneService extends BaseService<Phone> {//没有写属性
}

beans07.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"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.zzw.spring.depinjection"/>
</beans>
public class SpringBeanTest {@Testpublic void setProByDependencyInjection() {ApplicationContext ioc =new ClassPathXmlApplicationContext("beans07.xml");PhoneService phoneService = ioc.getBean("phoneService", PhoneService.class);phoneService.save();//PhoneDao的 save方法...System.out.println("OK");}
}

在这里插入图片描述

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

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

相关文章

P3373 【模板】线段树 2(乘法与加法)(内附封面)

【模板】线段树 2 题目描述 如题&#xff0c;已知一个数列&#xff0c;你需要进行下面三种操作&#xff1a; 将某区间每一个数乘上 x x x&#xff1b;将某区间每一个数加上 x x x&#xff1b;求出某区间每一个数的和。 输入格式 第一行包含三个整数 n , q , m n,q,m n,…

Flexbox

Flexbox 一、什么是 Flexbox ?二、Flexbox 知识点2.1、Flex Container&#xff08;容器&#xff09;2.1.1、轴2.1.2、添加flex支持2.1.3、flex-direction&#xff08;主轴向&#xff09;2.1.3.1、row 横向2.1.3.2、row-reverse 横向翻转2.1.3.3、column 纵向2.1.3.4、column-r…

宇凡微2.4g遥控船开发方案,采用合封芯片

2.4GHz遥控船的开发方案是一个有趣且具有挑战性的项目。这样的遥控船可以通过无线2.4GHz频率进行远程控制&#xff0c;让用户在池塘或湖泊上畅游。以下是一个简要的2.4GHz遥控船开发方案&#xff1a; 基本构想如下 mcu驱动两个小电机&#xff0c;小电机上安装两个螺旋桨&#…

速通pytorch库

速通pytorch库&#xff08;长文&#xff09; 前言 ​ 本篇文章主要为那些对于pytorch库不熟悉、还没有上手的朋友们准备&#xff0c;梳理pytorch库的主要内容&#xff0c;帮助大家入门深度学习最重要的库之一。 目录结构 文章目录 速通pytorch库&#xff08;长文&#xff09;1.…

pytorch学习——正则化技术——丢弃法(dropout)

一、概念介绍 在多层感知机&#xff08;MLP&#xff09;中&#xff0c;丢弃法&#xff08;Dropout&#xff09;是一种常用的正则化技术&#xff0c;旨在防止过拟合。&#xff08;效果一般比前面的权重衰退好&#xff09; 在丢弃法中&#xff0c;随机选择一部分神经元并将其输出…

openGauss学习笔记-28 openGauss 高级数据管理-NULL值

文章目录 openGauss学习笔记-28 openGauss 高级数据管理-NULL值28.1 IS NOT NULL28.2 IS NULL openGauss学习笔记-28 openGauss 高级数据管理-NULL值 NULL值代表未知数据。无法比较NULL和0&#xff0c;因为它们是不等价的。 创建表时&#xff0c;可以指定列可以存放或者不能存…

利用尺度因子方法恢复GRACE水储量变化

1.背景 重力恢复与气候实验&#xff08;GRACE&#xff09;观测地球重力势的时间变化。在考虑了大气和海洋效应后&#xff0c;每月到年际尺度上剩余的信号主要与陆地水储存&#xff08;TWS&#xff09;的变化有关。水储存变化的估计受到测量误差和噪声的信号退化影响&#xff0…

【机密计算-大厂有话说】NVIDIA Hopper H100 上的机密计算

1. 英伟达机密计算路线图(硬件) 在过去的四代中,NVIDIA 一直在不断提高安全性和设备的完整性。最早有文献记载的工作之一是在 NVIDIA V100 GPU 中,为设备上运行的固件提供了 AES 身份验证。身份验证可以保证用户可以信任启动固件没有被破坏,也没有被篡改。随着时…

剑指 Offer 54. ! 二叉搜索树的第k大节点 (考察二叉树的中序遍历)

剑指 Offer 54. 二叉搜索树的第k大节点 给定一棵二叉搜索树&#xff0c;请找出其中第 k 大的节点的值。 我的思路是&#xff1a;用一个全局arrayList不断收集“逆向”中序遍历该搜索二叉树所需要的答案 class Solution {int res, k;public int kthLargest(TreeNode root, int …

17、Spring6整合JUnit5

目录 17、Spring6整合JUnit5 17.1 Spring对JUnit4的支持 准备工作&#xff1a; 声明Bean spring.xml 单元测试&#xff1a; 17.2 Spring对JUnit5的支持 17、Spring6整合JUnit5 17.1 Spring对JUnit4的支持 准备工作&#xff1a; <?xml version"1.0" enco…

【TypeScript】类型断言的基本使用

类型断言的概念 有些时候开发者比TS本身更清楚当前的类型是什么&#xff0c;可以使用断言&#xff08;as&#xff09;让类型更加精确和具体。 类型断言&#xff08;Type Assertion&#xff09;表示可以用来手动指定一个值的类型。 类型断言语法&#xff1a; 值 as 类型 或 <…

如何使用STAR原则优化项目管理?

介绍STAR原则 1.1 STAR原则的定义 STAR原则是一个行为面试技术&#xff0c;即Situation&#xff08;情境&#xff09;、Task&#xff08;任务&#xff09;、Action&#xff08;行动&#xff09;和Result&#xff08;结果&#xff09;。这种原则被广泛应用在职业面试中&#x…

UG\NX 二次开发 选择相切面、相邻面的选择面控件

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan 简介&#xff1a; 有群友问“UFUN多选功能过滤面不能选择相切面或相邻面之类的吗&#xff1f;” 这个用Block UI的"面收集器"就可以&#xff0c;ufun函数是不行的。 效果&am…

【爬虫实践】使用Python从网站抓取数据

一、说明 本周我不得不为客户抓取一个网站。我意识到我做得如此自然和迅速&#xff0c;分享它会很有用&#xff0c;这样你也可以掌握这门艺术。【免责声明&#xff1a;本文展示了我的抓取做法&#xff0c;如果您有更多相关做法请在评论中分享】 二、计划策略 2.1 策划 确定您…

【CSS】3D卡片效果

效果 index.html <!DOCTYPE html> <html><head><title> Document </title><link type"text/css" rel"styleSheet" href"index.css" /></head><body><div class"card"><img…

12. Mybatis 多表查询 动态 SQL

目录 1. 数据库字段和 Java 对象不一致 2. 多表查询 3. 动态 SQL 使用 4. if 标签 5. trim 标签 6. where 标签 7. set 标签 8. foreach 标签 9. 通过注解实现 9.1 查找所有数据 9.2 通过 id 查找 1. 数据库字段和 Java 对象不一致 我们先来看一下数据库中的数…

java 定时任务不按照规定时间执行

这里写目录标题 排查代码中添加的定时任务步骤是否正确排查是否任务阻塞&#xff0c;如果定时任务出现异常阻塞后&#xff0c;将不会在次执行java中多个Scheduled定时器不执行为了让Scheduled效率更高&#xff0c;我们可以通过两种方法将定时任务变成多线程执行&#xff1a;方法…

nmake编译Qt第三方库出现无法打开包含文件type_traits

最近需要为个人项目ShaderLab添加内嵌的代码编辑窗口功能&#xff0c;支持语法高亮和Intellisense&#xff0c;最初使用了QCodeEditor,发现这个第三方的库对词法分析的实现效果不太行. 代码换行后直接缩进到首行&#xff0c;无法定位到前一句的首行 考虑换QScintilla&#xff…

阿里云ECS部署Mysql数据库

说明 首先需要到阿里云官方购买阿里云产品 &#xff0c;如果有机会可以免费试用那会更好&#xff0c;跳过购买云服务步骤下面直接演示。 一、阿里云官网示意图 1.百度搜索 阿里云官方 2.点击控制台 3.展开更多 4. 选择云服务器ECS 5. 点击实例 可以看到服务器状态&#xff…

Michael.W基于Foundry精读Openzeppelin第18期——DoubleEndedQueue.sol

Michael.W基于Foundry精读Openzeppelin第18期——DoubleEndedQueue.sol 0. 版本0.1 DoubleEndedQueue.sol 1. 目标合约2. 代码精读2.1 结构体Bytes32Deque2.2 length(Bytes32Deque storage deque) && empty(Bytes32Deque storage deque)2.3 at(Bytes32Deque storage de…