欢迎关注公众号“小东方不败”
创建一个新的模块,类型选择maven
:
导入依赖:目前只需要spring-context
和junit
两个依赖:
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.23version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
<scope>testscope>
dependency>
准备实体类和applicationContext.xml
:
其中applicationContext.xml
内容如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:util="http://www.springframework.org/schema/util"
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/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
beans>
里面有一些是之前学习时用到的约束说明,这里就不去掉了。如果想要创建“纯净”的spring的xml配置文件:
resources
目录—》new
—>XML Configuration File
—>Spring Config
要用xml注解方式创建,必须满足:项目中要有aop
依赖,但是我们上面的项目只导入了Context
依赖,没关系嘛?
其实
aop
依赖已经导入了:
下面介绍重点:使用注解方式配置
@Component
放在类上,用于标记,告诉spring当前类需要由容器实例化bean并放入容器中
该注解有三个子注解
@Controller
用于实例化controller层bean@Service
用于实例化service层bean@Repository
用于实例化持久层bean当不确定是哪一层,就用@Component
这几个注解互相混用其实也可以,但是不推荐
package com.bones.bean;
import org.springframework.stereotype.Component;
@Component
//如果要指定id: @Component("user1")
//如果不指定id,那就是类名首字母小写
public class Person {
}
package com.bones.bean;
import org.springframework.stereotype.Component;
@Component
public class User {
}
在配置文件applicationContext.xml
中配置包扫描
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:util="http://www.springframework.org/schema/util"
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/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<context:component-scan base-package="com.bones">
context:component-scan>
beans>
测试方法:
package com.bones.test01;
import com.bones.bean.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test1_Component {
@Test
public void TestComponent(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = applicationContext.getBean("user", User.class);
System.out.println(user);
}
}
成功获取到对象:
只扫描指定注解的包:
applicationContext.xml
<context:component-scan base-package="com.bones" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
context:component-scan>
注意:
use-default-filters
默认值是true
排除某一些注解扫描:
<context:component-scan base-package="com.bones" use-default-filters="true">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
context:component-scan>
如果没法扫描,但是强制从容器中获取对象的话,会报下面的错:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'person' available
准备代码:
UserDao
package com.bones.dao;
import org.springframework.stereotype.Repository;
@Repository
public interface UserDao {
int deleteUser();
}
UserDaoImplA
package com.bones.dao.impl;
import com.bones.dao.UserDao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDaoImplA implements UserDao {
@Override
public int deleteUser() {
System.out.println("UserDaoImplA");
return 0;
}
}
UserDaoImplB
package com.bones.dao.impl;
import com.bones.dao.UserDao;
import org.springframework.stereotype.Repository;
//@Repository
public class UserDaoImplB implements UserDao {
@Override
public int deleteUser() {
System.out.println("UserDaoImplB");
return 0;
}
}
UserService
package com.bones.service;
import org.springframework.stereotype.Service;
@Service
public interface UserService {
int deleteUser();
}
UserServiceImpl
package com.bones.service.impl;
import com.bones.dao.UserDao;
import com.bones.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public int deleteUser() {
userDao.deleteUser();
System.out.println("UserServiceImpl");
return 0;
}
}
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:util="http://www.springframework.org/schema/util"
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/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<context:component-scan base-package="com.bones" />
beans>
测试方法:
package com.bones.test01;
import com.bones.bean.User;
import com.bones.service.UserService;
import com.bones.service.impl.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test2_Autowired {
@Test
public void TestComponent(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = applicationContext.getBean("userServiceImpl", UserServiceImpl.class);
userService.deleteUser();
}
}
但是注意:
如果UserDaoImplA
和UserDaoImplB
如果都加上@Repository
注解的话,就会报错:
No qualifying bean of type 'com.bones.dao.UserDao' available: expected single matching bean but found 2: userDaoImplA,userDaoImplB
这时候,需要指定到底UserServiceImpl
中的属性UserDao
到底是实例化哪一个实现类,需要加注解:
@Qualifier(value="userDaoImplA")
可以简写为:
@Qualifier("userDaoImplA")
@Autowired
根据类型到容器中去寻找对应的对象,找到后给当前属性赋值
不需要依赖 set方法
属性类型可以是接口,会自动匹配对应的实现类对象
@Qualifier
根据属性名称注入依赖
注意:这个注解是JDK的注解,属于
javax.annotation.Resource
包
在有冲突的时候,也需要加上具体实例化的类:
@Resource(name = "userDaoImplB")
不可以简写。
前面体验了一下,实际强况下,很少会碰到这样的报错:
No qualifying bean of type 'com.bones.dao.UserDao' available: expected single matching bean but found 2: userDaoImplA,userDaoImplB
那么一般只需要写@Autowired
或者@Resource
,就可以了。那么到底写哪一个呢?
下面来详细说说:
源码在org.springframework.beans.factory.annotation
包下
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
从源码中可以看出,是加在这些元素上:
ElementType.CONSTRUCTOR,
ElementType.METHOD,
ElementType.PARAMETER,
ElementType.FIELD,
ElementType.ANNOTATION_TYPE
分别是:
构造方法
方法
方法的参数
成员变量
注解
总结:
1、@Autowired
是Spring自带的注解,通过AutowiredAnnotationBeanPostProcessor
类实现的依赖注入
2、@Autowired
可以作用在CONSTRUCTOR
、METHOD
、PARAMETER
、FIELD
、ANNOTATION_TYPE
3、@Autowired
默认是根据类型(byType
)进行自动装配的
4、如果有多个类型一样的Bean候选者,需要指定按照名称(byName
)进行装配,则需要配合@Qualifier
。
指定名称后,如果Spring IOC
容器中没有对应的组件bean抛出NoSuchBeanDefinitionException
。也可以将@Autowired
中required
配置为false
(其实一般配置为true也不影响运行,比如在Mybatis-plus
中),如果配置为false
之后,当没有找到相应bean的时候,系统不会抛异常
在javax.annotation
包下:
package javax.annotation;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
String name() default "";
String lookup() default "";
Class<?> type() default java.lang.Object.class;
enum AuthenticationType {
CONTAINER,
APPLICATION
}
AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
boolean shareable() default true;
String mappedName() default "";
String description() default "";
}
源码中说明了使用的区域:
TYPE, FIELD, METHOD
分别是:
接口、类、枚举、注解
成员变量
方法
总结:
1、@Resource
是JSR250
规范的实现,在javax.annotation
包下
2、@Resource
可以作用TYPE
、FIELD
、METHOD
上
3、@Resource
是默认根据属性名称进行自动装配的,如果有多个类型一样的Bean候选者,则可以通过name
进行指定进行注入,name 的作用类似 @Qualifier
@Resource(name = "userDaoImplB")
1、@Autowired
是Spring自带的,@Resource
是JSR250规范实现的(JDK自带的)
2、@Autowired
如果需要按照名称匹配需要和@Qualifier
一起使用,@Resource
则通过name
进行指定
Spring官方建议用@Autowired
(啥原因都懂),其实这两个一般不会有很大区别,随便用吧,但是估计@Autowired
用的会多一点。
(更深入的以后再分析)
更深的可以参考链接:https://blog.csdn.net/qq_35634181/article/details/104802906
@Value("爱因斯坦")
private String uname;
@Value("19")
private Integer uage;
如果想要从properties配置文件中读取,也可以:
@Value("${uname}")
private String uname;
@Value("${uage}")
private Integer uage;
其中配置文件内容:
uname=爱因斯坦
uage=18
同时在容器的配置文件中得读取properties配置文件:
<context:property-placeholder location="classpath:read.properties"/>
如果不希望有配置文件applicationContext.xml
(说实话,这么配置也怪麻烦的),可以创建一个config/SpringConfig
类,这个类专门用于扫描。
package com.bones.config;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan(basePackages = "com.bones")
public class SpringConfig {
}
测试:
package com.bones.test01;
import com.bones.config.SpringConfig;
import com.bones.service.UserService;
import com.bones.service.impl.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test2_Config_ComponentScan {
@Test
public void TestComponent(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = applicationContext.getBean("userServiceImpl", UserServiceImpl.class);
userService.deleteUser();
}
}
此时容器的创建,要调用的
AnnotationConfigApplicationContext
这个类,传入有扫描信息的类的字节码文件SpringConfig.class
。但是这种方式也有弊端,实际不怎么用,有一定局限性。
想要在@ComponentScan
配置的基础上,读取properties文件的话,需要加注解:
@PropertySource("classpath:XXX.properties")