Spring-IOC—基于注解配置Bean
1.基本使用
1.说明
● 基本介绍
基于注解的方式配置 bean, 主要是项目开发中的组件,比如 Controller、Service、和 Dao.
● 组件注解的形式有
-
@Component 表示当前注解标识的是一个组件 -
@Controller 表示当前注解标识的是一个控制器,通常用于 Servlet -
@Service 表示当前注解标识的是一个处理业务逻辑的类,通常用于 Service 类 -
@Repository 表示当前注解标识的是一个持久化层的类,通常用于 Dao 类
2.快速入门
● 应用实例
使用注解的方式来配置 Controller / Service / Respository / Component
● 代码实现
- 引入 spring-aop-5.3.8.jar , 在 spring/libs 下拷贝即可
- 创建 UserAction.java UserService.java, UserDao.java MyComponent.java
@Component
public class MyComponent {
}
@Controller
public class UserController {
}
@Repository
public class UserDao {
}
@Service
public class UserService {
}
beans.xml
<context:component-scan base-package="com.llp.spring.component"/>
测试
@Test
public void setBeanByAnnotation(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans05.xml");
UserDao userDao = ioc.getBean(UserDao.class);
UserService userService = ioc.getBean(UserService.class);
UserController userController = ioc.getBean(UserController.class);
MyComponent myComponent = ioc.getBean(MyComponent.class);
System.out.println(userDao);
System.out.println(userService);
System.out.println(userController);
System.out.println(myComponent);
}
3.注意事项和细节说明
1.需要导入 spring-aop-5.3.8.jar
2.必须在 Spring 配置文件中指定"自动扫描的包",IOC 容器才能够检测到当前项目中哪些类被标识了注解, 注意到导入 context 名称空间
xmlns:context=“http://www.springframework.org/schema/context”
<context:component-scan base-package="com.llp.spring.component"/>
可以使用通配符 * 来指定 ,比如 com.llp.spring.* 表示
提问: com.hspedu.spring.component 会不会去扫描它的子包? 会的
3.Spring 的 IOC 容器不能检测一个使用了@Controller 注解的类到底是不是一个真正的控制器。注解的名称是用于程序员自己识别当前标识的是什么组件。
其它的@Service、@Repository 也是一样的道理 [也就是说spring的IOC容器只要检查到注解就会生成对象,但是这个注解的含义 spring 不会识别,注解是给程序员编程方便看的]
4.resource-pattern=“User*.class”: 表示只扫描满足User打头的类.[使用的少,不想扫描,不写注解就可以, 知道这个知识点即可]
<context:component-scan base-package="com.llp.spring.component" resource-pattern="User*.class"/>
5.排除哪些类 , 以 annotaion 注解为例,如下就是排除@Service注解和@Repository类
<context:component-scan base-package="com.llp.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>
6.指定自动扫描哪些注解类
<context:component-scan base-package="com.llp.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:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
debug查看beanFactory中SingletonObjects对象中table属性的值
7.默认情况:标记注解后,类名首字母小写作为 id 的值。也可以使用注解的 value 属性指定 id 值,并且 value 可以省略。[代码演示]
@Controller(value = "user")
public class UserController {
}
@Test
public void setBeanByAnnotation(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans05.xml");
UserService userService = ioc.getBean("user",UserService.class);
System.out.println(userController);
}
2.手动开发-简单的Spring基于注解配置的程序
1.需求说明
- 自己写一个简单的 Spring 容器, 通过读取类的注解(@Component @Controller @Service @Reponsitory),将对象注入到 IOC 容器
- 也就是说,不使用 Spring 原生框架,我们自己使用 IO+Annotaion+反射+集合 技术实现, 打通 Spring 注解方式开发的技术痛点
2.思路分析
-
思路分析+程序结构 1)我们使用注解方式完成, 这里2不用 xml 来配置 2) 程序框架图
● 应用实例
- 手动实现注解的方式来配置 Controller / Service / Respository / Component
- 我们使用自定义注解来完成.
3.代码实现
1.搭建基本结构并获取扫描包
2.获取扫描包下所有.class文件
3.获取全类名 反射对象放入容器
自定义包扫描注解 @ComponentScan
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ComponentScan {
String value() default "";
}
配置类
@ComponentScan(value = "com.llp.spring.component")
public class LlpSpringConfig {
}
自定义容器-LlpSpringApplicationContext
public class LlpSpringApplicationContext {
private Class aClass;
private final ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();
public LlpSpringApplicationContext(Class aClass) {
this.aClass = aClass;
ComponentScan componentScan = (ComponentScan) aClass.getDeclaredAnnotation(ComponentScan.class);
String basePackage = componentScan.value();
System.out.println("basePackage=" + basePackage);
ClassLoader classLoader = LlpSpringApplicationContext.class.getClassLoader();
String path = basePackage.replace(".", "/");
System.out.println("path=" + path);
URL resource = classLoader.getResource(path);
System.out.println(resource);
File file = new File(resource.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
System.out.println("=========");
String absolutePath = f.getAbsolutePath();
System.out.println(f.getAbsolutePath());
String className = absolutePath.substring(absolutePath.lastIndexOf("\\") + 1, absolutePath.indexOf(".class"));
System.out.println(className);
String classFullName = path.replace("/", ".") + "." + className;
System.out.println(classFullName);
if (absolutePath.endsWith(".class")) {
try {
Class<?> cls = classLoader.loadClass(classFullName);
if (cls.isAnnotationPresent(Component.class) ||
cls.isAnnotationPresent(Controller.class) ||
cls.isAnnotationPresent(Service.class) ||
cls.isAnnotationPresent(Repository.class)) {
Class<?> clazz = Class.forName(classFullName);
Object o = clazz.newInstance();
String id = null;
if(cls.isAnnotationPresent(Component.class)){
Component component = cls.getAnnotation(Component.class);
id = component.value();
className = id;
}
ioc.put(id==null? StringUtils.uncapitalize(className):id,o);
System.out.println(ioc);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public Object getBean(String name){
return ioc.get(name);
}
}
测试类
public class LlpSpringApplicationContextTest {
public static void main(String[] args) {
LlpSpringApplicationContext ioc = new LlpSpringApplicationContext(LlpSpringConfig.class);
UserService userService = (UserService)ioc.getBean("userService");
UserDao userDao = (UserDao)ioc.getBean("userDao");
UserController userController = (UserController)ioc.getBean("userController");
MyComponent myComponent = (MyComponent)ioc.getBean("myComponent");
}
}
测试结果
3.自动装配
1.基本说明
● 基本说明
-
基于注解配置 bean,也可实现自动装配,使用的注解是:@AutoWired 或者 @Resource -
@AutoWired 的规则说明 1)在 IOC 容器中查找待装配的组件的类型,如果有唯一的 bean 匹配,则使用该 bean 装配 2)如待装配的类型对应的 bean 在 IOC 容器中有多个,则使用待装配的属性的属性名作为 id 值再进行查找, 找到就装配,找不到就抛异常 -
@Resource 的规则说明 1)@Resource 有两个属性是比较重要的,分别是 name 和 type,Spring 将@Resource 注解的 name 属性解析为 bean 的名字,而 type 属性则解析为 bean 的类型.所以如果使用 name 属 性,则使用 byName 的自动注入策略,而使用 type 属性时则使用 byType 自动注入策略 2)如果@Resource 没有指定 name 和 type ,则先使用byName注入策略, 如果匹配不上, 再使用 byType 策略, 如果都不成功,就会报错 -
建议,不管是@Autowired 还是 @Resource 都保证属性名是规范的写法就可以 注入
2.注意事项和细节说明
- 如待装配的类型对应的 bean 在 IOC 容器中有多个,则使用待装配的属性的属性名作 为 id 值再进行查找, 找到就装配,找不到就抛异常
4.泛型依赖注入
1.泛型依赖解释
● 基本说明
- 为了更好的管理有继承和相互依赖的 bean 的自动装配,spring 还提供基于泛型依赖的 注入机制
- 在继承关系复杂情况下,泛型依赖注入就会有很大的优越性
2.应用实例
● 应用实例需求
- 各个类关系图
- 传统方法是将 PhoneDao /BookDao 自动装配到 BookService/PhoneSerive 中,当这 种继承关系多时,就比较麻烦,可以使用 spring 提供的泛型依赖注入
● 应用实例-代码实现
public abstract class BaseDao<T> {
public abstract void save();
}
public class BaseService<T> {
@Autowired
private BaseDao<T> baseDao;
public void save() {
baseDao.save();
}
}
public class Book {
}
public class Phone {
}
@Repository
public class BookDao extends BaseDao<Book>{
@Override
public void save() {
System.out.println("BookDao 的 save()..");
}
}
@Repository
public class PhoneDao extends BaseDao<Phone>{
@Override
public void save() {
System.out.println("PhoneDao save()");
}
}
@Service
public class BookService extends BaseService<Book>{
}
@Service
public class PhoneService extends BaseService<Phone>{
}
@Test
public void setProByDependencyInjection() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("beans07.xml");
PhoneService phoneService = ioc.getBean("phoneService", PhoneService.class);
phoneService.save();
System.out.println("ok");
}
|