Spring系列三:基于注解配置bean

文章目录

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

上文中, 我们学习到了 Spring系列二:基于XML配置bean

接下来我们学习, 通过注解配置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");}
}

在这里插入图片描述
在这里插入图片描述下乘: Spring系列四:AOP切面编程

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

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

相关文章

【基于IDEA + Spark 3.4.1 + sbt 1.9.3 + Spark MLlib 构建逻辑回归鸢尾花分类预测模型】

逻辑回归进行鸢尾花分类的案例 背景说明&#xff1a; 基于IDEA Spark 3.4.1 sbt 1.9.3 Spark MLlib 构建逻辑回归鸢尾花分类预测模型&#xff0c;这是一个分类模型案例&#xff0c;通过该案例&#xff0c;可以快速了解Spark MLlib分类预测模型的使用方法。 依赖 ThisBui…

maven 删除下载失败的包

本文介绍了当Maven包报红时&#xff0c;使用删除相关文件的方法来解决该问题。文章详细说明了_remote.repositories、.lastUpdated和_maven.repositories文件的作用&#xff0c;以及如何使用命令行删除这些文件。这些方法可以帮助开发者解决Maven包报红的问题&#xff0c;确保项…

Linux 中利用设备树学习Ⅳ

系列文章目录 第一章 Linux 中内核与驱动程序 第二章 Linux 设备驱动编写 &#xff08;misc&#xff09; 第三章 Linux 设备驱动编写及设备节点自动生成 &#xff08;cdev&#xff09; 第四章 Linux 平台总线platform与设备树 第五章 Linux 设备树中pinctrl与gpio&#xff08;…

OBD针脚定义参考

OBD定义的一种标准的参考&#xff0c;不同的车场有不同的定义&#xff0c;貌似没有统一。 在某宝上看到的ODB转db9的不同的线序&#xff1a; 1&#xff09;1/2/3/6几个针脚都是一样的&#xff0c;分别上下针脚对应。 2&#xff09;其中一种4/5/7/8也是上下对应的&#xff1b;另…

自动化处理,web自动化测试处理多窗口+切换iframe框架页总结(超细整理)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 web 自动化之处理…

APP外包开发的学习流程

学习iOS App的开发是一项有趣和富有挑战性的任务&#xff0c;是一个不断学习和不断进步的过程。掌握基础知识后&#xff0c;不断实践和尝试新的项目将使您的技能不断提升。下面和大家分享一些建议&#xff0c;可以帮助您开始学习iOS App的开发。北京木奇移动技术有限公司&#…

小红书 KOL 种草执行策略揭秘:打造爆款产品,提升品牌影响力

随着互联网的普及和社交媒体的发展&#xff0c;小红书成为了众多年轻人购物决策的重要参考平台。小红书 KOL 种草作为一种新兴的营销方式&#xff0c;以其强大的传播力和影响力&#xff0c;越来越受到各大品牌的重视。本文伯乐网络传媒将给大家深入探讨小红书 KOL 种草的执行策…

mysql转sqlite3

在项目中需要将mysql迁移到sqlite3中&#xff0c;此时需要作数据转换 准备工作 下载mysql2sqlite转换工具 https://github.com/dumblob/mysql2sqlite/archive/refs/heads/master.zip 下载sqlite3 https://www.sqlite.org/download.html 转换 命令行中输入如下命令 1、cd …

海康威视摄像头二次开发_云台控制_视频画面实时预览(基于Qt实现)

一、项目背景 需求:需要在公司的产品里集成海康威视摄像头的SDK,用于控制海康威视的摄像头。 拍照抓图、视频录制、云台控制、视频实时预览等等功能。 开发环境: windows-X64(系统) + Qt5.12.6(Qt版本) + MSVC2017_X64(使用的编译器) 海康威视提供了设备网络SDK,设备网…

Zabbix监控系统详解及配置

前言 作为一个运维&#xff0c;需要会使用监控系统查看服务器状态以及网站流量指标&#xff0c;利用监控系统的数据去了解上线发布的结果&#xff0c;和网站的健康状态。利用一个优秀的监控软件&#xff0c;我们可以&#xff1a; 通过一个友好的界面进行浏览整个网站所有的服务…

Data analysis|Tableau基本介绍及可实现功能

一、基础知识介绍 &#xff08;一&#xff09;什么是tableau tableau 成立于 2003 年&#xff0c;是斯坦福大学一个计算机科学项目的成果&#xff0c;该项目旨在改善分析流程并让人们能够通过可视化更轻松地使用数据。Tableau可以帮助用户更好地理解和发现数据中的价值&#x…

虚拟机centos7配置网络

虚拟机centos7配置网络 centos7克隆之后需要配置网络才能联网。 实验环境&#xff1a; VMware Workstation Pro 16CentOS 7系统虚拟机主机Windows 11系统 1.VMware网络模式设置为NAT模式 虚拟机–设置–网络适配器– ​​ ‍ 2.查看虚拟机 子网IP和网关IP 编辑–虚拟网…

一、Webpack相关(包括webpack-dev-server用以热更新和html-webpack-plugin)

概念与功能&#xff1a; webpack是前端项目工程化的具体解决方案。它提供了友好的前端模块化开发支持&#xff0c;以及代码压缩混淆、处理浏览器端JavaScript的兼容性、性能优化等强大的功能。 快速上手&#xff1a;隔行变色 -S实际是--save的简写&#xff0c;表示安装的第三方…

判断是否在当前页面事件方法

页面可见性 页面可见性介绍 长期以来我们一直缺少一个判断用户是否正在浏览某个指定标签页的方法。用户是否去看别的网站了&#xff1f;他们切换回来了吗&#xff1f;现在html5里页面可见性接口就提供给了程序员一个方法&#xff0c;让他们使用visibilitychange页面事件来判断…

【Docker】Docker中network的概要、常用命令、网络模式以及底层ip和容器映射变化的详细讲解

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;陈童学哦&#xff0c;目前学习C/C、算法、Python、Java等方向&#xff0c;一个正在慢慢前行的普通人。 &#x1f3c0;系列专栏&#xff1a;陈童学的日记 &#x1f4a1;其他专栏&#xff1a;CSTL&…

C++入门(小白篇1)

前言&#xff1a; 最近想学一下一下C看了一些博客内容写的倒是很充实&#xff0c;但是&#xff0c;细节不到位&#xff0c;我是有Python基础的&#xff0c;所以学习来蛮快的&#xff0c;但是对于小白的话&#xff0c;有好多小细节大多数博客还是不够详细&#xff0c;由此我想写…

08. 容器间通信

目录 1、前言 2、容器间通信 2.1、通过IP地址进行通信 2.2、通过DNS Server进行通信 2.3、通过Joined方式通信 3、容器跨节点通信 3.1、通过容器在宿主机上的端口映射实现 3.2、通过Docker Overlay网络实现 4、小结 1、前言 上一篇《07.Docker网络通信模式》我们初步认…

python+django+mysql项目实践三(用户管理)

python项目实践 环境说明: Pycharm 开发环境 Django 前端 MySQL 数据库 Navicat 数据库管理 用户列表展示 urls view models html <!DOCTYPE html> <html

ES面试题

前言 1、面试突击正确的学习姿势 老师在给你讲面试突击的时候&#xff0c;是有课件的&#xff0c;而且是有准备的。你在面试的时候&#xff0c;是没有笔记课件的&#xff0c;而且问题是由面试官提问的&#xff0c;具有一定的随机性面试突击课程的目标不是听懂&#xff0c;而是…

深入解析微店详情API:提升电商平台的技术实力

了解微店详情API的基本概念和功能&#xff1a; 微店详情API是用于通过商品ID获取商品详情数据的接口。它提供了丰富的商品信息&#xff0c;包括商品名称、价格、描述、规格、图片等。我们将介绍API的请求和响应结构&#xff0c;以及常见的参数和返回字段。 最佳实践&#xff1…