基于注解实现
基于注解方式实现对象创建和注入属性
什么是注解
-
注解是代码的特殊标记,本身也是一个类,格式为@注解name(属性name=属性value[,…])。
-
注解可以使用在类、方法和属性上。
-
使用注解实现的目的是简化xml配置。
基于注解创建对象
Spring针对Bean管理中创建对象的步骤主要提供了四个注解:
@Component、@Service、@Controller、@Repository
上面的四个注解都可以创建对象(bean注入),但是后三个注解实际是@Component的衍生注解,分别推荐在service层、web层、dao层使用,四个注解功能实际是差不多的。
案例
下面我们通过一个案例来实现基于注解创建对象
引入依赖
使用注解需要额外引入依赖aop
该jar包在Spring目录下的lib文件夹中
把这个jar包放到我们手动创建的lib目录中,右键点击Add as Library即可完成导入:
开启组件扫描
在使用注解时我们还需要开启组件扫描,告诉Spring哪一个包的类需要用到注解
首先在配置文件中引入名称空间:
xmlns:context="http://www.springframework.org/schema/context"
然后在xsi:schemaLocation处添加:
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
组件扫描需要用到context:component-scan
标签,里面有一个base-package
属性,用于指明要扫描的包(这里创建了两个包用于后续代码编写):
<context:component-scan base-package="com.atguigu.spring5.dao, com.atguigu.spring5.service"/>
这里对dao包、service包进行扫描,包名之间用逗号分隔。
当然也可以直接写所有要扫描的包的共同的上层目录,比如dao和service的共同上层目录是com.atguigu.spring5:
<context:component-scan base-package="com.atguigu.spring5"/>
完整的配置文件如下:
<?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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.atguigu.spring5.dao, com.atguigu.spring5.service"/>
</beans>
创建类并添加创建对象注解
在service包里创建UserService类,如下所示:
@Component(value = "userService")
public class UserService {
public void add(){
System.out.println("service add...");
}
}
上面代码在UserService类前添加了@Component注解,表示该类基于注解创建对象
当然,写法也可以变成下面这样的:
@Component
public class UserService {
public void add(){
System.out.println("service add...");
}
}
这时注解的value的值默认是类名(当然,值的首字母会变成小写)。
编写测试代码:
@Test
public void testService(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
注意,这里的getBean的第一个参数,按照使用xml创建时,该参数写的是bean的id,而使用注解时,该参数写的是注解的value,也就是类名(首字母变小写)。
点击运行,看到如下结果:
证明bean已经被创建成功。
这一步主要是名称空间的引入,不能有差错
当然,@Component注解换成其他的注解,达到的效果也是一样的:
@Service
public class UserService {
public void add(){
System.out.println("service add...");
}
}
开启组件扫描的配置的一些细节
Spring的组件扫描默认是某个包下的全部类,但是有些时候我们只需要扫描某些类,又或者说不需要扫描某些类
use-default-filters
用于context:component-scan标签,默认值为true,如果设置其值false,表明由我们自己设置filters,也就是说由我们自己定义扫描范围
该属性结合context:include-filter使用,不结合context:exclude-filter使用。
context:include-filter
该标签表明只扫描的范围,主要属性有type,其值表示扫描的方式、expression,其值表示需要扫描的注解的类
context:exclude-filter
该标签表明不扫描的范围,主要属性有type,其值表示扫描的方式、expression,其值表示不需要扫描的注解的类,此时应该使用默认的filters。
为了方便案例展示,这里新建一个类PersonService,使用@Controller注解:
@Controller
public class PersonService {
public void person_add(){
System.out.println("PersonService add...");
}
}
同时编写测试代码:
@Test
public void testService(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
try {
PersonService personService = context.getBean("personService", PersonService.class);
personService.person_add();
} catch (Throwable e) {
System.out.println(e);
}
try {
UserService userService = context.getBean("userService", UserService.class);
userService.add();
} catch (Throwable e) {
System.out.println(e);
}
}
案例:只扫描某些类
下面是配置文件:
<?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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.atguigu.spring5" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
上面的标签的意思是只扫描com.atguigu.spring5下的有@Controller注解标识的类,type表明根据注解来扫描,expression表明扫描的注解是@Controller。
运行结果:
可以看到注入第二个bean时抛出了错误,这是因为我们只扫描了被@Controller注解的类。
案例:不扫描某些类
注意,如果使用context:exclude-filter标签,那么context:component-scan标签就不能设置use-default-filters=“false”。因为这是扫描全部类的基础上不扫描某些类。
<?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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.atguigu.spring5">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
上面配置标识不扫描被@Controller标识的类。
运行结果如图:
可以看到在注入第一个bean时抛出了错误,因为我们不扫描@Controller。
基于注解注入属性
Spring对于注入属性主要提供了四个注解:@Autowired、@Qualifier、@Value
java扩展包javax提供有注解:@Resource
自动注入其实跟自动装配差不多
@Autowired
根据属性类型自动注入
在dao层创建UserDao接口,内有add方法:
public interface UserDao {
public void add();
}
UserDao实现类(记得加上@Repository注解):
@Repository
public class UserDaoImpl implements UserDao{
@Override
public void add(){
System.out.println("dao add...");
}
}
以往我们通过xml,在UserService类拿到UserDaoImpl类时,需要分别创建两个bean,用外部bean的方式去注入,现在我们只需要在UserService类中用@Autowired即可:
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void add(){
System.out.println("service add...");
userDao.add();
}
}
add方法中调用UserDao的add方法(多态)。
注意,这个时候被注入的字段不需要添加set方法
测试方法:
@Test
public void testService(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
配置文件:
<?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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.atguigu.spring5"/>
</beans>
下面是运行结果:
可以看到已经实现了UserDao的注入。
@Qualifier
根据属性名称自动注入
这个注解要和@Autowired一起使用
在上面使用@Autowired的过程中,加入一个dao接口有多个实现类,那Spring就不知道我们到低需要哪一个了,这个时候就要结合@Qualifier注解来使用了。
比如我新建一个集成UserDao接口的userDaoImpl1类:
@Repository
public class UserDaoImpl1 implements UserDao{
@Override
public void add(){
System.out.println("dao add...");
}
}
这个时候如果我没加上@Qualifier注解那么就会报错:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userDao'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.atguigu.spring5.dao.UserDao' available: expected single matching bean but found 2: userDaoImpl,userDaoImpl1
加上该注解,指定注入的实现类:
@Autowired
@Qualifier(value = "userDaoImpl1")
private UserDao userDao;
这个时候再运行就不会出现错误了:
@Resource
根据属性名称/属性类型自动注入,根据name判定
name属性:不设置时为根据类型注入,设置时根据其值作为名称来注入
根据名称注入
依旧沿用上面创建的项目,下面是根据名称进行注入,把UserDao上面的注解换成@Resource(name = “userDaoImpl1”),表明注入的实现类是名称为userDaoImpl1的类:
@Service
public class UserService {
@Resource(name = "userDaoImpl1")
private UserDao userDao;
public void add(){
System.out.println("service add...");
userDao.add();
}
}
运行结果:
没毛病。
根据类型注入
根据类型进行注入,先把UserDaoImpl1实现类删了免得产生混淆,然后修改UserService类:
@Service
public class UserService {
@Resource
private UserDao userDao;
public void add(){
System.out.println("service add...");
userDao.add();
}
}
运行测试代码:
没问题。
可以看到根据类型注入属性比较适合懒人,在实际开发中最好是要求一个接口只有一个实现类,如果要求多个实现类那最好对接口进行再分隔,这样就能主动避免冲突问题,也不需要麻烦地去定义一些注解的属性。
@Value
注入普通类型属性,比如String
@Value(value = "abd")
private String name;
上面的代码为name属性赋值“abc”。
纯注解开发
在上面使用注解的案例中我们可以发现,要想实现这些操作的前提还是得有一个xml文件来扫描整个项目,现在我们需要的是去掉xml,之依靠注解来进行开发。
想要去除xml,我们得有一个能替代配置文件的东西——配置类。
配置类主要用到@Configuration:表示该类是配置类、@ComponentScan:表示要扫描的包
在开发目录下创建一个config包(与启动类同级),用于存放配置类,在包下创建SpringConfig类(名字随便起),然后类上添加注解@Configuration和@ComponentScan,其中@ComponentScan需要配置参数,值为扫描的包,下面是完整的配置类:
@Configuration
@ComponentScan(basePackages = {com.atguigu.spring5})
public class SpringConfig {
}
是的你没看错,配置类啥也不需要写,老工具人了
这个时候你就可以删掉xml配置文件了,当然,这个时候运行你会发现项目报错,这是因为测试方法的原因。
此时测试方法还用着xml文件获取bean
重写测试方法:
@Test
public void testService2(){
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
观察上面的代码我们可以发现,new的对象不在是ClassPathXmlApplicationContext
,而是AnnotationConfigApplicationContext
,里面的参数为configname.class,也就是SpringConfig.class。
下面运行测试方法:
不错,完美
发现没,如果你是从Springboot入门的,那么现在已经有Springboot的味道了。