什么是Spring:
据度娘所载:Spring是一个开源框架,Spring是于2003年兴起的一个轻量级的Java开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EEfull-stack(一站式)轻量级开源框架。Spring的核心是控制反转(IoC)和面向切面(AOP)。简单来说,Spring是一个分层的JavaSE/EEfull-stack(一站式)轻量级开源框架。
为什么说Spring是一个一站式的轻量级开源框架呢?
首先轻量级:轻量级不是指Spring框架的模块少,数量很轻,这里的轻量级是指Spring框架的非侵入性,意思是说开发应用中的对象可以不必依赖Spring的API类
其次,JavaEE开发可分成三层架构,针对JavaEE的三层结构,每一层Spring都提供了不同的解决技术。
WEB层:SpringMVC
业务层:Spring的IoC
持久层:Spring的JDBCTemplate(Spring的JDBC模板,ORM模板用于整合其他的持久层框架)
从上面的简要介绍中,我们要知道Spring的核心有两部分:
IoC:控制反转。
举例来说,在之前的操作中,比方说有一个类,我们想要调用类里面的方法(不是静态方法),就要创建类的对象,使用对象调用方法实现。对于Spring来说,Spring创建对象的过程,不是在代码里面实现的,而是交给Spring来进行配置实现的。
AOP:面向切面编程。
在Spring学习过程中,我们会着重来讲它。
为什么用Spring:
- 方便解耦,简化开发。
Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护,交给Spring管理。 - AOP编程的支持
Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。 - 声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无须手动编程。 - 方便程序的测试
Spring对Junit4支持,可以通过注解方便的测试Spring程序。 - 方便集成各种优秀的框架
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts2、
Hibernate、MyBatis、Quartz等)的直接支持。 - 降低JavaEE API的使用难度
Spring对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了
封装,使这些API应用难度大大降低。 - Spring的版本
Minimum requirements
JDK 8+ for Spring Framework 5.x
JDK 6+ for Spring Framework 4.x
JDK 5+ for Spring Framework 3.x
学习spring的顺序
----要先学spring ,springmvc,mybatis,hibernate,jpa,springdatajpa后在学springboot基础,然后还有学linux,docker,zookeeper。。。。。在学springboot高级框架整合,前面的课是后面的课的基础,不建议直接从springboot开始,你搞不懂的,就像不学初等数学直接学高等数学一样。
----sts已经集成好的,现在spring官网已经不提供eclipse的spring插件了,spring官网,以后只会提供springboot的开发模式了,也就是说,源码spring的单独使用是我们学习的一个小终点,但是以后,spring的学习只是我们学习springboot的基础了,技术在飞速的进步,我们也必须得赶上!
spring使用的环境搭建。
1. STS3.94/ 4.11
本人用的是STSIDE已集成了spring.
最低JDK版本求:查看HELP-ABOUT spring tools suite 4.1---Configureation
注:本人测试jpa时,jdk8是可行的,jdk11要加入以下依赖包(maven工程中):
javax.xml.bind
jaxb-api
2.3.0
com.sun.xml.bind
jaxb-impl
2.3.0
com.sun.xml.bind
jaxb-core
2.3.0
javax.activation
activation
1.1.1
org.openjfx
javafx-controls
11
2. ECLIPSE:
查看ECLIPSE版本号:
*****下载Eclipse注意所需JDK版本是JDK1.7还是JDK1.8等,否则Eclipse无法正常启动,弹出错误框“Version 1.7.0_17 of the JVM is not suitable for this product.Version: 1.9 or greater is required”信息。
----springsource-tool-suite插件 是一个基于Eclipse的开发环境,为开发Spring应用程序而定制。它提供了一个即用的环境来实现,调试,运行和部署Spring应用程序,包括Pivotal tc服务器(企业版本的Apache Tomcat),Pivotal CloudFoundry,Git,Maven,AspectJ的集成。
<21> springsource-tool-suite插件压缩包下载安装:
http://spring.io/tools/sts/all下图中所示的下载地址已404了。本人在网上找到了一下载地址。
最插件压缩包下载安装:
在线通过链接安装(通过改变版本号与Eclipse相符,进行在线安装)
本人在网上找到了一下载地址:
eclipse4.7.3a版本:
http://download.springsource.com/release/TOOLS/update/3.9.4.RELEASE/e4.7/springsource-tool-suite-3.9.4.RELEASE-e4.7.3a-updatesite.zip
eclipse4.7.3版本:
http://download.springsource.com/release/TOOLS/update/3.9.3.RELEASE/e4.7/springsource-tool-suite-3.9.3.RELEASE-e4.7.3-updatesite.zip
eclipse4.7.2版本:
http://download.springsource.com/release/TOOLS/update/3.9.2.RELEASE/e4.7/springsource-tool-suite-3.9.2.RELEASE-e4.7.2-updatesite.zip
eclipse4.7.1a版本:
http://download.springsource.com/release/TOOLS/update/3.9.1.RELEASE/e4.7/springsource-tool-suite-3.9.1.RELEASE-e4.7.1a-updatesite.zip
eclipse4.7.0版本:
http://download.springsource.com/release/TOOLS/update/3.9.0.RELEASE/e4.7/springsource-tool-suite-3.9.0.RELEASE-e4.7.0-updatesite.zip
eclipse4.6.3版本:
http://download.springsource.com/release/TOOLS/update/3.8.4.RELEASE/e4.6/springsource-tool-suite-3.8.4.RELEASE-e4.6.3-updatesite.zip
eclipse4.6.2版本:
http://download.springsource.com/release/TOOLS/update/3.8.3.RELEASE/e4.6/springsource-tool-suite-3.8.3.RELEASE-e4.6.2-updatesite.zip
eclipse4.6.1版本:
http://download.springsource.com/release/TOOLS/update/3.8.2.RELEASE/e4.6/springsource-tool-suite-3.8.2.RELEASE-e4.6.1-updatesite.zip
eclipse4.6版本:
http://download.springsource.com/release/TOOLS/update/3.8.1.RELEASE/e4.6/springsource-tool-suite-3.8.1.RELEASE-e4.6-updatesite.zip
http://download.springsource.com/release/TOOLS/update/3.8.0.RELEASE/e4.6/springsource-tool-suite-3.8.0.RELEASE-e4.6-updatesite.zip
eclipse4.5.2版本:
http://download.springsource.com/release/TOOLS/update/3.7.3.RELEASE/e4.5/springsource-tool-suite-3.7.3.RELEASE-e4.5.2-updatesite.zip
eclipse4.5.1版本:
http://download.springsource.com/release/TOOLS/update/3.7.2.RELEASE/e4.5/springsource-tool-suite-3.7.2.RELEASE-e4.5.1-updatesite.zip
http://download.springsource.com/release/TOOLS/update/3.7.1.RELEASE/e4.5/springsource-tool-suite-3.7.1.RELEASE-e4.5.1-updatesite.zip
eclipse4.5版本:
http://download.springsource.com/release/TOOLS/update/3.7.0.RELEASE/e4.5/springsource-tool-suite-3.7.0.RELEASE-e4.5-updatesite.zip
eclipse4.4.2版本:
http://download.springsource.com/release/TOOLS/update/3.6.4.RELEASE/e4.4/springsource-tool-suite-3.6.4.RELEASE-e4.4.2-updatesite.zip
eclipse4.4.1版本(SR1):
http://download.springsource.com/release/TOOLS/update/3.6.3.SR1/e4.4/springsource-tool-suite-3.6.3.SR1-e4.4.1-updatesite.zip
eclipse4.4.1版本:
http://download.springsource.com/release/TOOLS/update/3.6.3.RELEASE/e4.4/springsource-tool-suite-3.6.3.RELEASE-e4.4.1-updatesite.zip
http://download.springsource.com/release/TOOLS/update/3.6.2.RELEASE/e4.4/springsource-tool-suite-3.6.2.RELEASE-e4.4.1-updatesite.zip
eclipse4.4版本:
http://download.springsource.com/release/TOOLS/update/3.6.1.RELEASE/e4.4/springsource-tool-suite-3.6.1.RELEASE-e4.4-updatesite.zip
http://download.springsource.com/release/TOOLS/update/3.6.0.RELEASE/e4.4/springsource-tool-suite-3.6.0.RELEASE-e4.4-updatesite.zip
eclipse4.3.2版本:
http://download.springsource.com/release/TOOLS/update/3.5.1.RELEASE/e4.3/springsource-tool-suite-3.5.1.RELEASE-e4.3.2-updatesite.zip
http://download.springsource.com/release/TOOLS/update/3.5.0.RELEASE/e4.3/springsource-tool-suite-3.5.0.RELEASE-e4.3.2-updatesite.zip
eclipse4.3.1版本:
http://download.springsource.com/release/TOOLS/update/3.4.0.RELEASE/e4.3/springsource-tool-suite-3.4.0.RELEASE-e4.3.1-updatesite.zip
eclipse4.3版本:
http://download.springsource.com/release/TOOLS/update/3.3.0.RELEASE/e4.3/springsource-tool-suite-3.3.0.RELEASE-e4.3-updatesite.zip
eclipse4.2.2版本:
http://download.springsource.com/release/TOOLS/update/3.2.0.RELEASE/e4.2/springsource-tool-suite-3.2.0.RELEASE-e4.2.2-updatesite.zip
eclipse4.2版本:
http://download.springsource.com/release/TOOLS/update/3.1.0.RELEASE/e4.2/springsource-tool-suite-3.1.0.RELEASE-e4.2-updatesite.zip
http://download.springsource.com/release/TOOLS/update/3.0.0.RELEASE/e4.2/springsource-tool-suite-3.0.0.RELEASE-e4.2-updatesite.zip
1 离线安装springsource-tool-suite插件压缩包
打开Eclipse -> Help -> Install Software -> Add,操作示意图:
2 在线安装springsource-tool-suite插件
打开Eclipse -> Help -> Install Software -> Add,在“Localtion...”项文
本框添加地址:http://dist.springsource.com/release/TO......,具体操作示意图:
不要安装太多,下图给出了几个项。
个人建议直接使用离线下载的spring-tool-suite和ecipse整合好的工具包(即就是 STSIDE),将解压包解压后打开..\sts-bundle\sts-3.7.2.RELEASE目录,启动STS.exe程序即可。目录结构如图:
测试安装成功否:
打开ECLIPSE-NEW ---ANOTHER---- 若有如下:spring等项,说明安装成功。
spring开发。
本开发建一个maven web项目:springdemo
不从骨架中选架子项目。最开始时,Dynamic Web Module是2.5版,这也太扯了,现在都4.x时代了,但我们JDK8选 3.1就好了。
右击项目--properties---Project facets
需要的包:
org.springframework
spring-context
5.0.6.RELEASE
log4j
log4j
1.2.12
commons-logging
commons-logging-api
1.1
1 IOC
--IOC:Inversion of Control,控制反转。指的是对象的创建权反转(交给)给Spring,其作用是实现了程序的解耦合。也可这样解释: 获取对象的方式变了 。对象创建的控制权不是“使用者”,而是“框架”或者“容器”。
用更通俗的话来说,IOC就是指对象的创建,并不是在代码中用new操作new出来的,而是通过Spring进行配置创建的。其底层实现原理是XML配置文件+SAX解析+工厂设计模式。
--就拿我们在前面讲JavaWeb入门中的MVC结构的持久层 [ 也即dao(dataaccess object,数据访问对象)层 ] 的开发来说,官方推荐做法是先创建一个接口,然后再创建接口对应的实现类。
传统方法::
Dao层:
1.UserDao.java(接口)
import cn.ybzy.springdemo.model.User;
public interface UserDao {
public void add(User user);
}
2.UserDaoImpl.java(UserDao实现类)
import cn.ybzy.springdemo.model.User;
public class UserDaoImpl implements UserDao{
@Override
public void add(User user) {
System.out.println("dao层被调用了");
System.out.println(user);
}
}
3.DaoFactory.java(工厂类,减少耦合)
public class DaoFactory {
public static UserDao getUserDao() {
return new UserDaoImpl();
}
}
Service层
1.UserService.java(接口)
import cn.ybzy.springdemo.model.User;
public interface UserService {
public void add(User user);
}
2.UserServiceImpl.java(UserService实现类)(本例中又作测试用--有main()方法)
public class UserServiceImpl implements UserService {
//UserDao userDao = new UserDaoImpl();//不用工厂模式,是非常紧密的耦合。
UserDao userDao = DaoFactory.getUserDao(); //如若这样做,会发现又产生了一个缺点:service层和工厂类又耦合了。所以使用工厂模式进行解耦合也只是一种权宜之计。
//我们修改上面的代码,利用Spring框架,来让Service层和Dao层彻底解耦合!
@Override
public void add(User user) {
System.out.println(user);
}
public static void main(String[] args) {
User user =new User();
user.setUserName("zs");
user.setPassword("123");
UserDao userDao =new UserDaoImpl();
userDao.add(user); //这程传统的做法,是有非常紧密的耦合
}
}
spring这么干实现上面的测试::
不用DaoFactory.java彻底没了有Dao--Service的耦合。
applicationContext.xml
测试:
public class UserServiceImpl implements UserService {
//UserDao userDao = new UserDaoImpl();//不用工厂模式,是非常紧密的耦合。
UserDao userDao = DaoFactory.getUserDao(); //如若这样做,会发现又产生了一个缺点:service层和工厂类又耦合了。所以使用工厂模式进行解耦合也只是一种权宜之计。
//我们修改上面的代码,利用Spring框架,来让Service层和Dao层彻底解耦合!
@Override
public void add(User user) {
System.out.println(user);
}
public static void main(String[] args) {
//spring里的做法
//1.拿到spring的ioc容器。
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.通过这个容器,我们可以拿到我们想要的对象寮例。
User user = ctx.getBean(User.class);
UserDao userDao =ctx.getBean(UserDao.class);
//3.获取到的对象实例的变量调用类里的方法
userDao.add(user);
}
1 就上面的入门案例对解耦合进一步分析和总结
在我们的日常开发中
,创建对象的操作随处可见以至于对其十分熟悉的同时又感觉十分繁琐,每次需要对象都需要亲手将其new出来,甚至某些情况下由于坏编程习惯还会造成对象无法被回收,这是相当糟糕的。但更为严重的是,我们一直倡导的松耦合,少入侵原则,这种情况下变得一无是处。于是前辈们开始谋求改变这种编程陋习,考虑如何使用编码更加解耦合,由此而来的解决方案是面向接口的编程,于是便有了如下写法:
UserServiceImpl
类中由原来直接与UserDaoImp打交互变为UserDao,即使UserDao最终实现依然是UserDaoImp,这样的做的好处是显而易见的,所有调用都通过接口userDao来完成,而接口的真正的实现者和最终的执行者就是UserDaoImpl,当替换UserDaoImpl类,也只需修改userDao指向新的实现类。
虽然上述的代码在很大程度上降低了代码的耦合度,但是代码依旧存在入侵性和一定程度的耦合性,比如在修改UserDao的实现类时,仍然需求修改UserServiceImpl的内部代码,当依赖的类多起来时,查找和修改的过程也会显得相当糟糕.所以工厂设计模式,又被提了出来,我们把这种有可能变化的部分,放到Dao层的工厂类里,UserDao是实现类变了,我就对应修改Dao里的工厂类里对象的方法,这样就不用修改UserServiceImpl类的内部代码了!
虽然上述的代码进一步地降低了代码的耦合度,但是代码依旧存在入侵性和一定程度的耦合性,那就是UserServiceImpl类和Dao层里的工厂类之间,仍然耦合着, 修改工厂类后,UserServiceImpl就得跟着改,所以呢, 前辈们继续考虑着.......
Rod Johnson提出了控制反转IOC和依赖注入DI的概念:
------这种方法如我们所愿生成了userDao的实例,这样做的好处是在有替换userDao实现类的情况只需修改配置文件的内容而无需触及UserServiceImpl的内部代码,从而把代码修改的过程转到配置文件中,相当于UserServiceImpl及其内部的userDao通过配置文件与UserDao的实现类进行关联,这样UserServiceImpl与UserDao的实现类
UserDaoImpl间也就实现了解耦合,当然UserServiceImpl类中存在着UserDao对象是
无法避免的,毕竟这是协同工作的基础,我们只能最大程度去解耦合。
这里我提出了一个新的概念依赖注入 :所谓的依赖注入,其实是当一个bean实例引用到了另外一个bean实例时spring容器帮助我们创建依赖bean实例并注入(传递)到另一个bean中,如上述案例中的UserServiceImpl类依赖于UserDao的实现类UserDaoImpl,Spring容器会在创建UserService的实现类和UserDao的实现类后,把UserDao的实现类注入UserService实例中,有关依赖注入后面还要详细讲的。
2 SpringIOC容器Bean之XML配置方式
-
首先看applicationContext.xml里的配置项bean:
我们采用xml配置文件的方式对bean进行声明和管理,每一个bean标签都代表着需要被创建的对象并通过property标签可以为该类注入其他依赖对象,通过这种方式Spring容器就可以成功知道我们需要创建那些bean实例
- ApplicationContext----SpringIOC的容器
然后通过ClassPathXmlApplicationContext去加载spring的配置文件,接着获取想要的实例bean并调用相应方法执行。对于ClassPathXmlApplicationContext默认加载classpath路径下的文件,只需指明对应文件的classpath路径下的配置文件名字即可。如果存在多个配置文件,ClassPathXmlApplicationContext是一个可以接收可变参数的构造函数。实际上ClassPathXmlApplicationContext还有一个孪生兄弟FileSystemXmlApplicationContext,它默认为项目工作路径 即项目的根目录 ,至于使用哪个,个人觉得没多大的差别 。
这里, 不管用按哪个方法去获取spring的配置文件, 返回的都是一个ApplicationContext,Spring的IOC的容器, 但实际上ApplicationContext是一个接口:
这里的ConfigurableApplicationContext子接口,给我们提供了一些方法close(),refresh(), 可以让ApplicationContext刷新和关闭的方法, 后面要用到,这里先认识一下。
ApplicationContext在初始化的时候, 就实例化所有单列的Bean。具体的从ApplicationContext容器中获取对象实例的方法getBean:
注意: 平时,我都是用id值来获取的, 虽然从xxx.class也可以获取, 但是用这个有个限制: ApplicationContext只有一个这个类型的对象实例, 才能用, 否则会报错!
- 依赖注入。
Spring通过标签实现依赖注入, Spring支持的注入方式有三种:
①. 属性注入
---属性注入, 也叫Setter注入
Setter注入顾名思义,被注入的属性需要有set方法, Setter注入支持简单类型和引用类型,Setter注入是在bean实例创建完成后执行的。直接观察前面的案例,对象注入使用<property>的ref属性,对象注入同时也可以注入简单值和map、set、list、数组。简单值注入使用<property>的value属性。
例: Map属性注入:
public class User {
private int id;
private String userName;
private String password;
private Map map =new HashMap<>();
public Map getMap() {
return map;
}
public void setMap(Map map) {
this.map = map;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public User() {
super();
}
public String toString() {
return "User [id=" + id + ", userName=" + userName + ", password=" + password + "]";
}
}
配置文件xml中:
UserServiceImpl.java
public class UserServiceImpl implements UserService {
/**
* 需要注入的对象(依赖注入,没有set方法注入不了)
*/
private UserDao userDao;
private String testName;
/**
* 配置文件中通过set方法将userDao这个对象注入进来的
* @param userDao
*/
public void setUserDao(UserDao userDao,String testName) {
this.userDao=userDao;
this.testName=testName;
}
public void setTestName(String testName) {
this.testName = testName;
}
@Override
public void add(User user) {
userDao.add(user);
//userDao.add(user);
}
//测试,用到mian方法,也可以junit测试
public static void main(String args[]) {
User user=new User();
user.setUserName("熊少文");
user.setPassword("123456");
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService=(UserService)context.getBean("userService");
//配置文件中已依赖注了入userDao.
userService.add(user);
}
/**
* 构造方注入时用到
* @param userDao
*/
public UserServiceImpl(UserDao userDao,String testName) {
System.out.println("构造方法1111111111111");
this.userDao = userDao;
this.testName=testName;
}
public UserServiceImpl(String testName,UserDao userDao) {
System.out.println("构造方法22222222222222222222");
this.userDao = userDao;
this.testName=testName;
}
public UserServiceImpl(UserDao userDao) {
this.userDao=userDao;
}
/**
* 测试userDao依赖注入成功否,不成功显null
* 后来加入构造方中
*/
@Override
public void display() {
System.out.println("注入的userDao:::"+userDao);
System.out.println("注入的testName:::"+testName);
}
}
测试:
public class SpringTest {
@Test
public void test() {
ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml"); //context是IOC容器
//注:可以放多个配置文件new ClassPathXmlApplicationContext("applicationContext.xml","xxxxx.xml","fsdfsdf.xml");
UserService userService=(UserService)context.getBean("userService");
//获取IOCP容器中的类(已注入了username,password等属性)
User user1=(User) context.getBean("user"); //"user"是配置文件中的id
User user2=context.getBean(User.class); //与上一代码效果一样,但上一条代码很常用,因为当配了多个对象时,该代码会出错。
//Map类型注入测试
System.out.println("user1:"+user1+" "+"user2:"+user2);
System.out.println("map中的各科课成绩"+user1.getMap());
}
}
②. 构造器注入
----构造注入也就是通过构造方法注入依赖,构造函数的参数一般情况下就是依赖项,spring容器会根据bean中指定的构造函数参数来决定调用那个构造函数,同样看上一个案例。
当然跟setter注入一样,构造注入也可传入简单值类型和集合类型,这个比较简单,不啰嗦。 需要注意的是,当一个bean定义中有多个<constructor-arg>标签时,它们的放置顺序并不重要,因为Spring容器会通过传入的依赖参数与类中的构造函数的参数进行比较,尝试找到合适的构造函数。在某些情况下,如某个类,带有两个构造函数,参数类型和个数都是一样的,只是顺序不同,这在class的定义中是允许的,但对于Spring容器来说默认会只会去调用前面的。
----如果我们要指定使用哪个构造方法也是可以的,在<constructor-arg>标签中存在一个index的属性,通过index属性可以告诉spring容器传递的依赖参数的顺序,下面的配置将会令Spring容器成功找到第二个构造函数并调用创建实例。
----在日常的开发中,setter注入和构造注入经常会混合使用, 构造注入中index和type也可以混合使用,这并不用感觉到诧异,后面我们还会分析到注解装配,它在开发中将更为常用。
测试:
@Test
public void test() {
ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml"); //context是IOC容器
//注:可以放多个配置文件new ClassPathXmlApplicationContext("applicationContext.xml","xxxxx.xml","fsdfsdf.xml");
//ClassPathXmlApplicationContext还有一个孪生兄弟FileSystemXmlApplicationContext,它默认为项目工作路径 即项目的根目录 ,至于使用哪个,个人觉得没多大的差别 。
UserService userService=(UserService)context.getBean("userService");
//获取IOCP容器中的类(已注入了username,password等属性)
User user1=(User) context.getBean("user"); //"user"是配置文件中的id
User user2=context.getBean(User.class); //与上一代码效果一样,但上一条代码很常用,因为当配了多个对象时,该代码会出错。
//Map类型注入测试
/* System.out.println("user1:"+user1+" "+"user2:"+user2);
System.out.println("map中的各科课成绩"+user1.getMap());*/
//引用变量注入的测试(userDao为引用型)
/*userService.display(); //显示 cn.ybzy.springdemo.dao.UserDaoImpl@5bcea91b表示userDao成功注入,不然为null */
//测试,构造方法注入效果(UserServiceImpl有多个构造方法public UserServiceImpl(UserDao userDao,String testName,....)再配置xml文件中
userService.add(user1); //dao层被调用了说明可行。
userService.display();
}
--------------------------------------------------------------------------------------------------------
测试结果:
服务层调用了我,dao层被调用了
服务层调用了我,显示用户(注入的)信息为: User [id=1, userName=admin, password=admin123]
注入的userDao:::cn.ybzy.springdemo.dao.UserDaoImpl@3891771e
注入的testName:::xiongshaowen混合注入
③工厂方法注入(很少使用, 也不推荐使用,这个就不讲了)
-
Spring容器Bean之XML配置属性的细节
1、简单值注入使用<property>的value属性可以换一种写法
2、 简单值注入使用<property>的value属性值中有些特殊的字符,比如< 、> 的时
候可以用包裹
3、内部bean
前面的配置都是注入关联外部的bean, 内部bean也就是相对于外部bean而言的, 注意
内部bean是不能被外面引用的:
如:UserDao由UserService调用,可以配这种内部bean,其它外部都用不了
4、配置Property类型的属性值
5、配置独立的集合bean,让多个bean去引用,会用到utility scheme将前面list,map,set这些集合的配置,拿出来,称为一个独立的单元,可以被多个bean引用:
增加命名空间:
测试:上面的User bean Map属性可以这样改一下。
改成为::::::
6、使用p命名空间
同过p命名空间,可以简化我们对bean的配置, 需要导入p的scheme:
测试:
改为::::
3 自动注入(装备)
直接示例:
- 三个类 Person Pet Car 人,宠物,车
public class Person {
private int id;
private Pet pet;//宠物
private List cars;
private double petPrice;;
pirvate String state;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Pet getPet() {
return pet;
}
public void setPet(Pet pet) {
this.pet = pet;
}
public List getCars() {
return cars;
}
public void setCars(List cars) {
this.cars = cars;
}
public Person() {
super();
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public double getPetPrice() {
return petPrice;
}
public void setPetPrice(double petPrice) {
this.petPrice = petPrice;
}
@Override
public String toString() {
return "Person [id=" + id + ", pet=" + pet + ", cars=" + cars + ", petPrice=" + petPrice + "]";
}
}
public class Pet {
private int id;
private String type;
private double price;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Pet() {
super();
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price =price;
}
@Override
public String toString() {
return "Pet [id=" + id + ", type=" + type +",petPrice= "+price+"]";
}
}
public class Car {
private int id;
private String mark;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getMark() {
return mark;
}
public void setMark(String mark) {
this.mark = mark;
}
public Car() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Car [id=" + id + ", mark=" + mark + "]";
}
}
- 配置xml文件
- 测试类:AutowireTest.java
public class AutowireTest {
@Test
public void test() {
@SuppressWarnings("resource")
ApplicationContext context= new ClassPathXmlApplicationContext("applicationTest.xml"); //context是IOC容器
Person person= (Person)context.getBean("person");
System.out.println(person);
}
}
在依赖注入上除了前面我们讲的手动注入的情况,Spring还非常智能地为我们提供自动向Bean注入依赖的功能,这个过程一般被称为 自动装配(autowiring) 。这是一个非常酷的功能,当注入的bean特别多时,它将极大地节省编写注入程序的时间,因此在开发中,非常常见。但是个人来讲,我自己还是不太喜欢用自动装配,怕出错!我喜欢在代码中明确写清楚!
4 配置Bean自己的关系:继承和依赖
继承: 这里的继承不是Java中类之间的继承, 是指配置文件中Bean配置项之间的继承。
可写成下图代码:
-------用parent属性,配置要继承的bean,这样可以把相同的部分去去掉,下上两个bean的关系就变成了父bean和子bean, 子bean可以继承父bean属性, 也可以覆盖父bean的属性。
------父bean可以被配置为一个模板,只用来被继承,不被实例化,这样只要配置一个abstract属性值为true就可以了
注意,配置了abstract属性的抽象bean,必要的话,class属性都可以不配置的!
依赖:可以用depends-on属性,显示声明,这个bean必须依赖另一个bean,如果过这个被依赖的bean没配置的化,Spring容器就报错!
测试:
ApplicationContext context1= new ClassPathXmlApplicationContext("ApplicationExtendsTest.xml");
Pet cat =(Pet)context1.getBean("cat1");
Person person=(Person)context1.getBean("person");
// System.out.println(cat);
System.out.println(person);
5 配置文件xml怎么读取外部属性文件。
举例:数据库连接池与连接测试
测试连接一下吧:
若控制台显示com.mchange.v2.c3p0.impl.NewProxyConnection@4278a03f说明连接成功。
//mysql连接测试,数据源为一个bean对象。数据库为spring5。
DataSource dataSource = (DataSource)context.getBean("dataSource");
System.out.println(dataSource.getConnection());
如把数据库连接放在配置文件xml中,强烈不建议讲数据源配置在Spring的配置文件里,因为,这里会配置很多bean,而我们的数据连接的信息呢? 开发完成后部署到生产环境是一定为修改的,迁移服务器时也会去修改,所以放到Spring的配置文件里,查找不是很方便,所以我们在实际项目中的一般做法是,将数据连接池的配置信息都是放到properties这种属性文件里的,单独放配置起来方便,不用找!怎么在Spring配置文件中读取到外部的属性文件里的数据值?
数据库版本和种类是经常改的,放在xml文件中,打包后,会看不到该文件,而properties文件是可以看,很方便动态修改。
-
命名空间选中context
6 Spring的表达式语言spEL
***Spring表达式语言(SpEL):是一个支持运行时查询和操作对象图的强大表示是语言,是一种可以与一个基于spring的应用程序中的运行时对象交互的东西。总得来说SpEL表达式是一种简化开发的表达式, 通过使用表达式来简化开发,减少一些逻辑、配置的编写。
语法类似于 EL:SpEL 使用 #{...} 作为定界符 , 所有在大括号中的字符都将被认为是SpEL , SpEL 为 bean 的属性进行动态赋值提供了便利(如上面的动态读取外部文件中的属性)。
通过 SpEL 可以实现:
1.通过 bean 的 id 对 bean 进行引用,用了SpEL在bean标签中可以用value代替ref。
2.可以像EL一样用点运算符调用方法以及对象中的属性。
3.计算表达式的值
4.正则表达式的匹配
SpEL 字面量,意义不大,spring内部本身有数据类型的自动转换机制,直接写值就好了,不必用SqEL,了解:
整数:#{8}
小数:#{8.8}
科学计数法:#{1e4}
String:可以使用单引号或者双引号作为字符串的定界符号。
Boolean:#{true}
SpEL引用bean , 调用它属性和方法:
1.引用其他对象:#{car}
2.引用其他对象的属性:#{car.price}
3.调用其它方法 , 还可以链式操作:#{person.pet.toString()}
4.调用静态方法静态属性:#{T(java.lang.Math).PI}
5.Spring EL 操作List、Map集合取值
SpEL支持的运算符号:
算术运算符:+,-,*,/,%,^(加号还可以用作字符串连接)
比较运算符:< , > , == , >= , <= , lt , gt , eg , le , ge
逻辑运算符:and , or , not , |
if-else 运算符(类似三目运算符):?:(temary), ?:(Elvis)
正则表达式:#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.
[a-zA-Z]{2,4}'} 返加一个布尔值
测试:
@Test
public void test() {
ApplicationContext context=new ClassPathXmlApplicationContext("spELapplication.xml");
Person person =(Person) context.getBean("person");
System.out.println(person); //Person [id=1, pet=Pet [id=1, type=CAT, petPrice=1000.0], cars=null, petPrice=1000.0]
System.out.println(person.getState());
}
返回结果:
Person [id=1, pet=Pet [id=1, type=CAT, petPrice=1000.0], cars=null, petPrice=1000.0]
有钱人
// true
7 bean的生命周期。
默认情况下
,Spring在读取xml文件的时候,就会创建对象。在创建对象的过程是:先调用构造器,然后有属性值调用set方法设置属性,然后调用init-method属性值中所指定的初始化方法。对象在被销毁的时候,会调用destroy-method属性值中所指定的销毁方法,写一个测试类,代码如下:
LifeBean.java:
public class LifeBean {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
System.out.println("setId()调用了");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("lifeBean的setName()调用了");
}
public void init() {
System.out.println("lifeBean的init初始化方法");
}
public void destroy() {
System.out.println("liefBean的destroy销毁方法");
}
public LifeBean() {
super();
System.out.println("构造方法调用了");
}
@Override
public String toString() {
return "LifeBean [id=" + id + ", name=" + name + "]";
}
}
LifeCyclyapplication.xml:
MyBeanPostProcessor.java:
这是实现了BeanPostProcessor接口的类。
// BeanPostProcessor是一个接口,定义两个方,可以不实现。这里只是测试生命周期的过程
public class MyBeanPostProcessor implements BeanPostProcessor{
//前置处理,init-method指定的初始化方法执行之前,调用下面一个方法
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("nit-method指定的初始化方法执行之前postProcessBeforeInitialization");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
//后置处理,init-method指定的的初始化方法执行之后,调用下面一个方法
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("init-method指定的的初始化方法执行之后postProcessAfterInitialization");
LifeBean lifeBean=(LifeBean) bean; //初始化后预处理,此时已创建了LifeBean对象lifeBean了,
lifeBean.setName("hahaha"); //修改已创建对象属性值,原值为:lifebeanName。
return BeanPostProcessor.super.postProcessAfterInitialization(lifeBean, beanName); //返回的改为已创建的bean
}
}
测试方法:
@Test
public void test() {
ConfigurableApplicationContext configcontext= new ClassPathXmlApplicationContext("LifeCyclyapplication.xml");
LifeBean lifeBean = (LifeBean)configcontext.getBean("lifeBean");
System.out.println(lifeBean);
System.out.println("lifeBeanm的名字:"+lifeBean.getName());
configcontext.close();
}
测试结果:
构造方法调用了
setId()调用了
lifeBean的setName()调用了
nit-method指定的初始化方法执行之前postProcessBeforeInitialization
lifeBean的init初始化方法
init-method指定的的初始化方法执行之后postProcessAfterInitialization
lifeBean的setName()调用了
LifeBean [id=1, name=hahaha]
lifeBeanm的名字:hahaha
liefBean的destroy销毁方法
测试中,
我们可以看spring中的bean的简单生命周期,实际上它的完整生命周期要复杂一些:
我们这里对
BeanPostProcessor接口,做一个实现类
,演 示一下这些接口如果要在项目中自定义的话应该怎么用,生命周期的前后置处理方法的执行情况:
在实际开中,我们一般不会修改已写好的代码,一般增加代码是可以的,但要修改我们针对此功能中,我们可以在BeanPostProcess接口类中作一些可能的修改。
8 Spring通过工厂方法进行配置bean.
在Spring的世界中, 我们通常会利用 xml配置文件 或者 annotation注解方式来配置bean实例!
在第一种利用 xml配置文件 方式中, 还包括如下三小类
1.反射模式(我们前面的所有配置都是这种模式)
2.工厂方法模式
3.Factory Bean模式
其中反射模式最常见, 我们需要在bean 配置中配置我们需要的bean object的全类名。
上面bean 里面的class属性就是全类名, Spring利用java反射机制创建这个bean object。
工厂方法模式
----在工厂方法模式中, Spring不会直接利用反射机制创建bean对象, 而是会利用反射机制先找到Factory类,然后利用Factory再去生成bean对象。而Factory Mothod的具体使用方式也分两种, 分别是静态工厂方法 和 实例工厂方法。
- 静态工厂方法方式
**所谓静态工厂方式就是指Factory类不本身不需要实例化, 这个Factory类中提供了1个静态方法来生成bean对象
首先,建一个模型类Car-一定要有有参构造方法。
public class Car {
private int id;
private String mark;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getMark() {
return mark;
}
public void setMark(String mark) {
this.mark = mark;
}
public Car() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Car [id=" + id + ", mark=" + mark + "]";
}
public Car(int id, String mark) {
super();
this.id = id;
this.mark = mark;
}
}
其次,建一个工厂类CarStaticFactory,在该类中,建Car对象。
public class CarStaticFactory {
private static Map cars = new HashMap<>();
static {
cars.put("1", new Car(1,"Audi"));
cars.put("2", new Car(2,"BWM"));
cars.put("3", new Car(3,"BC"));
}
public static Car getCar(String key) {
return cars.get(key);
}
}
再次,xml配置文件 applicationCarFactory.xml中:
-->
最后测试:
@Test
public void test() {
ConfigurableApplicationContext context= new ClassPathXmlApplicationContext("applicationCarFactory.xml");
Car bwnCar =(Car) context.getBean("BWNCar"); //静态工厂模式获取注入的对象信息
//Car bwnCar =(Car) context.getBean("bwn"); //实例化工厂模式
System.out.println(bwnCar);
}
- 实例化工厂模式配bean.
这里,只列举实例化工厂类CarInstanceFactory.java,其它要用的与上面一样,去掉注即可。
public class CarInstanceFactory {
private Map cars = new HashMap<>();
public void setCars(Map cars) {
this.cars = cars;
}
public Car getCar(String key) {
return cars.get(key);
}
}
小结:
由上面的例子, 静态工厂方法方式是非常适用于作为1个bean容器, 只不过bean集合定义在工厂类里面而不是项目xml配置文件里面。缺点也比较明显, 把数据写在class里面而不是配置文件中违反了我们程序猿的常识和spring的初衷。当然优点就是令人恶心的xml配置文件更加简洁。所以,工厂方法的配置,了解一下就行了,个人建议不要在项目中使用。
显然,实例化工厂方法比静态工厂方法,要灵活一些,没把数据写死在工厂类里,但是实际开发中,用的最多的还是反射模式!
- FactoryBean配置bean
------spring通过FactoryBean配置,比前面的工厂方法配置Bean要重要些,因为我们整合很多第三方的框架的时候,需要用到FactoryBean来配置第三方框架中的bean 对象,从而把第三方框架整合到spring中来!当然在整合这些第三方框架的时候,这个 FactoryBean一般都是我们引入的jar包中,人家给写好了的,我们会用就行,但知道原理也是好的!
自定义的FactoryBean需要实现FactoryBean接口
1.配置bean实例(以Car模型为示例)。
首先实现FactoryBean接口,MyFactoryBean.java:
//类中的两个方法一定要写上,不然出错
public class FactoryBeanCar implements FactoryBean{
private String type; //定义一个属性,等会测试看效果
public FactoryBeanCar(String type) {
this.type=type;
}
//返回我们要配置的bean 对象。
@Override
public Car getObject() throws Exception {
return new Car(1,type);
}
//返回我们配置的bean 对象的类型。
@Override
public Class> getObjectType() {
return Car.class;
}
}
其次xml配置文件中,创建一个车对象
< bean id="bwm" class="cn.ybzy.springdemo.model.FactoryBeanCar">
value="BWM"是FactoryBeanCar一个一参构造方法的参数值
测试:
@Test
public void test() {
ConfigurableApplicationContext context= new ClassPathXmlApplicationContext("applicationCarFactory.xml");
//Car bwnCar =(Car) context.getBean("BWNCar"); //静态工厂模式获取注入的对象信息
Car bwnCar =(Car) context.getBean("bwm");
System.out.println(bwnCar);
}
测试结果:
Car [id=1, mark=BWM]
2.通过整合Quartz框架,实现定时任务
21.首先建任务类
先导包
org.quartz-scheduler
quartz
2.2.1
org.slf4j
slf4j-api
1.7.5
org.slf4j
slf4j-nop
1.7.5
org.springframework
spring-tx
5.0.10.RELEASE
再建任务类 MoJob implements Job
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("quartz的具体每次执行的工作任务"); //这里只是做一个测试,任务是输出一段文字
}
}
22.applicationCarFactory.xml配置文件中创建(配置)注入,任务对象,触 发器,调度器
测试:
该测试要到main方法中执行,所以定义一个RunTest.java(cn.ybzy.springdemo.model.RunTest.java
public static void main(String args[]) {
ConfigurableApplicationContext context= new ClassPathXmlApplicationContext("applicationCarFactory.xml");
SchedulerFactoryBean scheduler = (SchedulerFactoryBean)context.getBean("scheduler");
scheduler.start();
}
9 用注解的方式来配置Bean
在实际项目开发中,可能使用注解配置Bean,使用的还要广泛一些,因为更方便简洁!
传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop、事务,这么做有两个缺点:
1、如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大;如果按需求分开.xml文件,那么.xml文件又会非常多。总之这将导致配置文件的可读性与可维护性变得很低
2、在开发中在.java文件和.xml文件之间不断切换,是一件麻烦的事,同时这种思维上的不连贯也会降低开发的效率为了解决这两个问题,Spring引入了注解,通过"@XXX"的方式,让注解与Java Bean紧密结合,既大大减少了配置文件的体积,又增加了Java Bean的可读性与内聚性。
- Spring 注解配置初始化对象(
):
spring中使用注解配置对象前,要在配置文件中配置context:component-scan 标签告诉spring框架,配置了注解的类的位置 配置文件applicationContext.xml:
- 注解说明:
Component最初spring框架设计的,后来为了标识不同代码层,衍生出Controller,Service,Repository三个注解 作用相当于配置文件的bean标签,被注解的类,spring始化时,就会创建该对象
@Component("user") // 不知是什么层时的给类注解为bean,没指定里面的字符串,默认该对象名为类名,并小写,再放进IOC容器里,后可从容器里获取该对象。
@Service("user") // service层
@Controller("user") // web业务层
@Repository("user")//dao层
@Scope(scopeName="singleton") 等同于配置文件的scope属性
@Value(value="188") //给值属性赋值,可以用在方法上或者属性上
@Resource(name="car") //给对象赋值,该值car必须要已经声明(在配置文件中已经配置,或者在类对应中已经注解)
@PostConstruct //指定该方法在对象被创建后马上调用 相当于配置文件中的init-method属性
@PreDestroy //指定该方法在对象销毁之前调用 相当于配置文件中的destory-method属性
@Autowired //自动装配对象赋值@Qualifier("car2") 一起使用 告诉spring容器自动装配哪个对象
@Autowired顾名思义,就是自动装配,其作用是为了消除代码Java代码里面的getter/setter与bean属性中的property。当然,getter看个人需求,如果私有属性需要对外提供的话,应当予以保留。这里@Autowired注解的意思就是,当Spring发现@Autowired注解时,将自动在代码上下文中找到和其匹配(默认是类型匹配)的Bean,并自动注入到相应的地方去。因此,引入@Autowired注解,不要忘记配置文件要写:
@Autowired注解要去寻找的是一个Bean,Tiger和 Monkey的Bean定义都给去掉了,Spring容器找不到了自然抛出异常。那么,如果属性找不到对应的对象我不想让Spring容器抛 出异常,而就是显示null,可以吗?可以的,就是将@Autowired注解的required属性设置为false 即可:
那么如果有一个接口,有多个实现,Bean里引用的是接口名,又该怎么做呢?比如有一个Car接口两个实现类 Audi ,Bwm:
public interface Car {
public String getCarName();
}
@Component
public class Audi implements Car{
@Override
public String getCarName() {
return "奥迪";
}
}
@Component
public class Bwm implements Car{
@Override
public String getCarName() {
return "宝马";
}
}
注入Car接口名,由于它有两个实现类,@Autowired不知如何选 择,会报异常。解决办法:《1》,删除实现类,只留一个实现类。《2》,用@Qualifier("audi")选择要注入的实现对象。
@Component
public class Zoo {
//@Resource(name="tigera") //等价于xml文件中配置ref="tigera",给属性赋值,该值为一个引用对象。
@Autowired(required=false) //使无bean时(即没有注解配bean也没有xml配bean时或去掉注解了),返回null,不会产生异常。
private Tiger tiger;
@Autowired(required=false) //自动注入放注解,比@Resource节省很多set,get代码 required=false 让注入空时即IOC里没有bean对象时不报异常,只报null.
private Monkey monkey;
@Autowired
@Qualifier("audi") //选中实现Car接口的类Audi,对象注入到ioc中。
private Car car;
@Override
public String toString() {
return "Zoo [tiger=" + tiger + ", monkey=" + monkey + ",car="+car+"]";
}
测试:
public class RunTest {
public static void main(String args[]) {
//测试定时任条,FactoryBean接口实现类与Job一起实现
/*ConfigurableApplicationContext context= new ClassPathXmlApplicationContext("applicationCarFactory.xml");
SchedulerFactoryBean scheduler = (SchedulerFactoryBean)context.getBean("scheduler");
scheduler.start();*/
//注解配置bean放进IOC容器里,再从IOC中获取对象示例。Zoon,Tiger,Monkey
ConfigurableApplicationContext context= new ClassPathXmlApplicationContext("ZhujieApplication.xml");
Tiger tiger = (Tiger) context.getBean("tigera"); //Tiger中只加了一个注解时的获取@Component
System.out.println(tiger); //Tiger [tigerName=null]加了(@Value("东北虎")属性值,就会显示值。
Zoo zoo = (Zoo) context.getBean("zoo");
System.out.println(zoo);
}
}
****最后提一下,还有一个功能和@Autowired差不多的一个注解@inject,它是jsr330规范的注解,用它的话要导入相应的jar包
javax.inject
javax.inject
1
,我们推荐使用@Autowired.
- 举例:
先看一个不使用注解的Spring示例,在这个示例的基础上,改成注解版本的,这样也能看出使用与不使用注解之间的区别,
首先定义一个动物园
public class Zoo {
private Tiger tigger;
private Monkey monkey;
public Tiger getTigger() {
return tigger;
}
public void setTigger(Tiger tigger) {
this.tigger = tigger;
}
public Monkey getMonkey() {
return monkey;
}
public void setMonkey(Monkey monkey) {
this.monkey = monkey;
}
@Override
public String toString() {
return "Zoo [tigger=" + tigger.getTigerName() + ", monkey=" + monkey.getMonkeyName() + "]";
}
}
其后,定义一个老虎:
再定义一个猴子:
用注解:(xxx.xml中只一条代码就够了,如下图。
也可以:
说一下@Resource的装配顺序:
1、@Resource后面没有任何内容,默认通过name属性去匹配bean,找不到再按type去匹配
2、指定了name或者type则根据指定的类型去匹配bean
3、指定了name和type则根据指定的name和type去匹配bean,任何一个不匹配都将报错
注解之component-scan标签详解
首先,这标签是需要context的命名空间的。
base-package: 指定spring扫描注解的类所在的包。当需要扫描多个包的时候,可以使用逗号分隔。
-
如果只希望扫描特定的类,不是扫描包里的所有类的时候,可以使用resource-pattern属性来指定只扫描的包。
这样配置,除了User的对象,其他都找不到了(因为此时model中只有User)!
-
使用排除子节点:
这样配置,@controller注解的类的对象就找不到了!
-
使用context:include-filter和use-default-filters="false"配合使用
除了包含的注解以外的注解的类的对象都找不到了!
-
上面都是用的type=annotation,下面在看一下assignable
排除UserDao这个接口以及这个接口的实现类!include-file类似就不演示了!
注解之泛型注入。
Spring4以后有的功能。
泛型依赖注入就是允许我们在使用spring进行依赖注入的同时,利用泛型的优点对代码进行精简,将可重复使用的代码全部放到一个类之中,方便以后的维护和修改。同时在不增加代码的情况下增加代码的复用性。
举例: mvc 服务层调用数据层功能
- 传统方法中--没有spring配bean,手动new 对象。
1.cn.ybzy.springdemo.model包中两个POJO模型类 User,Authority(权限)。
public class User {
private int id;
private String userName;
private String password;
get set方法
无参构造一个,
toString()方法
}
---------------------------------------------------------------------------------------------------------------
/权限类POJO
public class Authority {
private int id;
private String authorityName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAuthorityName() {
return authorityName;
}
public void setAuthorityName(String authorityName) {
this.authorityName = authorityName;
}
public Authority() {
super();
}
@Override
public String toString() {
return "Authority [id=" + id + ", authorityName=" + authorityName + "]";
}
}
2.cn.ybzy.springdemo.dao包(UserDao,UserDaoImpl,AuthorityDao,AuthorityDaoImpl,BaseDao
public class BaseDao {
public void add(T t) {
//一般来说,先会数据库连接,再添加记录,这里只是测试用,简单一条代码。
System.out.println("Dao层正在添加数据到数据库中");
}
//显示对象
public void display(T t) {
System.out.println(t);
}
}
-------------------------------------------------------------------------------------------
UserDao接口与 UserDaoImpl--- UserDao接口的实现类
public interface UserDao {
public void add(User user);
//显示对象
public void display(User user);
}
public class UserDaoImpl extends BaseDao implements UserDao{
@Override
public void add(User user) {
System.out.println("UserDaoImpl----服务层调用了我,dao层被调用了");
}
//可不用实现UserDao接口的add(),display(),因为父类BaseDao已实现了,直接用父类的方法,写泛型类就这点好,节省大量代码。
}
--------------------------------------------------------------------------------------------------------------
AuthorityDao接口与实现 AuthorityDaoImpl
public interface AuthorityDao {
public void add(Authority authority);
public void display(Authority authority);
}
public class AuthorityDaoImpl extends BaseDao implements AuthorityDao {
@Override
public void add(Authority authority) {
System.out.println("AuthorityDaoImpl里的add方法");
}
//可不用实现AuthorityDao接口的add(),display(),因为父类BaseDao已实现了,直接用父类的方法。
}
- cn.ybzy.springdemo.service服务层(UserService,UserServiceImpl,AuthorityService,AuthorityServiceImpl,BaseService)
public class BaseService {
protected BaseDao baseDao;
}
--------------------------------------------------------------------------------------------------------------
public interface UserService {
public void add(User user);
}
public class UserServiceImpl extends BaseService implements UserService {
private UserDao userDao=new UserDaoImpl(); //当然了,再进步一点是用工厂类获取UserDao实现类对象,这是不写了,见mvcproject项目吧。
public void add(User user) {
System.out.println("UserServiceImpl里add的方法");
userDao.add(user);
System.out.println(baseDao); //null,baseDao对象没产生
// baseDao.add(user); //真正应用dao层这这样的,BaseService有一个成员变量 protected BaseDao baseDao;
//baseDao.add(user) 不用注解配bean的话,直接放在此会产生空指针异常,因为在这里还不是参数泛型User对象
}
}
-------------------------------------------------------------------------------------------------------------------
public interface AuthorityService {
public void add(Authority authority);
}
public class AuthorityServiceImpl extends BaseService implements AuthorityService {
private AuthorityDao authorityDao=new AuthorityDaoImpl(); //当然了,再进步一点是用工厂类获取UserDao实现类对象,这是不写了,见mvcproject项目吧。
@Override
public void add(Authority authority) {
System.out.println("AuthorityServiceImpl里的add方法");
authorityDao.add(authority);
System.out.println(baseDao); //null,baseDao对象没产生
// baseDao.add(authority); //真正应用dao层这这样的,BaseService有一个成员变量 protected BaseDao baseDao;
//baseDao.add(user) 不用注解配bean的话,直接放在此会产生空指针异常,因为在这里还不是参数泛型Authority对象
}
}
- 测试: cn.ybzy.springdemo.model.RunTest.java
public static void main(String args[]){
UserDao userDao = new UserDaoImpl();
AuthorityDao authorityDao = new AuthorityDaoImpl();
User user = new User();
user.setId(1);
user.setUserName("CEXOIT");
user.setPassword("122345456");
Authority authority = new Authority();
authority.setAuthorityName("管理员");
authority.setId(1);
UserService userService=new UserServiceImpl();
userService.add(user);
userDao.display(user);
System.out.println("------------------------------------------------------------------------------------------------------------------");
AuthorityService authorityService=new AuthorityServiceImpl();
authorityService.add(authority);
authorityDao.display(authority);
}
}
测试结果:
UserServiceImpl里add的方法
UserDaoImpl---服务层调用了我,dao层被调用了
null
User [id=1, userName=CEXOIT, password=122345456]
------------------------------------------------------------------------------------------------------------------
AuthorityServiceImpl里的add方法
AuthorityDao被服务支调用了的add方法
null
Authority [id=1, authorityName=管理员]
- 注解配bean对象且泛型注入方式,实现mvc 服务层调用数据层功能。
在1.的基础上修改一些类:
对象模型类,BaseDao,相关接口不修改。
Dao层:
import cn.ybzy.springdemo.model.User;
@Repository("userDao") //把UserDaoImpl对象放进容器里名字为userDao,以后不用new了,可获取使用,。
public class UserDaoImpl extends BaseDao implements UserDao{
//因为了BaseDao,我们把所有共公功能(添加记录,删除记录,悠改记录等)的代码提取到一起,节省了其它Dao对象的代码,所以下面的add,display方法在此处可以不写
}
import cn.ybzy.springdemo.model.Authority;
@Repository("authorityDao") //Dao层注解
public class AuthorityDaoImpl extends BaseDao implements AuthorityDao {
@Override
public void add(Authority authority) {
System.out.println("AuthorityDao被服务支调用了的add方法");
}
}
Service层:
public class BaseService {
@Autowired //自动注入泛型对象,这是可以的,spring 4以后的功能。
//这里只是测试,我没有写add,display方法,像BaseDao那样,这里是为了节省 创建 daoImpl对象。
public BaseDao baseDao;
}
@Service("userService")
public class UserServiceImpl extends BaseService implements UserService {
//有了BaseService泛型注入,下面注释的代码不用写了,自动生成baseDao对象到IOC中。
/* @Autowired //自动注入userDao接口实现类对象,UserDaoImpl中已加了@Repository("userDao")
private UserDao userDao; //自动注入对象(不用new了),没注入要写上 private UserDao userDao = new UserDaoImpl();
*/
@Override
public void add(User user) {
System.out.println("UserServiceImpl里add的方法");
baseDao.add(user);//真正应用dao层这这样的,BaseService有一个成员变量 protected BaseDao baseDao;
System.out.println(baseDao); //null,baseDao对象没产生
//baseDao.add(user) 不用注解配bean的话,直接放在此会产生空指针异常,因为在这里还不是参数泛型User对象
}
}
@Service("authorityService")
public class AuthorityServiceImpl extends BaseService implements AuthorityService {
//有了泛型注入,下面注释的代码不用写了,自动生成baseDao对象到IOC中。
/*@Autowired
private AuthorityDao authorityDao; //自动注入对象(不用new了),没注入要写上 private AuthorityDao userDao = new AuthorityDaoImpl();
*/
@Override
public void add(Authority authority) {
System.out.println("AuthorityServiceImpl里的add方法");
baseDao.add(authority);
System.out.println(baseDao);
}
}
***测试泛型注入:RunTest.java---main()中 ***
ConfigurableApplicationContext context= new ClassPathXmlApplicationContext("ZhujieApplication.xml");
AuthorityService authorityService =(AuthorityService) context.getBean("authorityService");
UserService userService = (UserService)context.getBean("userService");
User user = new User();
user.setId(1);
user.setUserName("CEXOIT");
user.setPassword("122345456");
userService.add(user);
UserDao userDao =(UserDao) context.getBean("userDao");
userDao.display(user);
2 AOP(面向切面编程)
AOPAOP面向切面编程
3 JdbcTemplate访问数据库
有要和数据库打交道了,首先在原来的基础上添加jar包,建一个测试数据库pring5,里边新建两个表users,authorities,user_authority:
- 导包
org.springframework
spring-jdbc
5.0.6.RELEASE
org.springframework
spring-orm
5.0.6.RELEASE
org.springframework
spring-tx
5.0.6.RELEASE
-
然后在spring的配置文件里,配置数据源和JdbcTemplate,然后需要jdbc.properties文件,还有测试类:
- 做查,增,删,改测试。
public static void main(String args[])throws SQLException {
ApplicationContext context = new ClassPathXmlApplicationContext("jdbc.xml");
//DataSource dataSource = (DataSource) context.getBean("dataSource");
//System.out.println(dataSource); //测试连接成功否
JdbcTemplate jdbcTemplate = (JdbcTemplate)context.getBean("jdbcTemplate"); //JdbcTemplate 比hibernate弱爆了,不能查关联表数据据
//查询数据
//查询一条记录,返回的结果是一个对应的对象。
/*String sql = "SELECT id,username,password FROM users where id=?"; //查询时,最好不要写*
RowMapper rowMapper=new BeanPropertyRowMapper<>(User.class);
User user =jdbcTemplate.queryForObject(sql,rowMapper,1);
System.out.println(user);*/
//查询多条记录
/*String sql = "SELECT id,username,password from users where id>?";
RowMapper rowMapper = new BeanPropertyRowMapper<>(User.class);
List users = jdbcTemplate.query(sql, rowMapper,0);
System.out.println(users);*/
//查询某一个列的值。
String sql = "SELECT COUNT(id) from users";
String sql2= "SELECT username from users where id=?";
String username= jdbcTemplate.queryForObject(sql2, String.class,1);
long count = jdbcTemplate.queryForObject(sql, Long.class);
System.out.println("第一个学生的姓名为:"+username);
System.out.println("总共有"+count+"条记录");
//增加数据到库
/*String sql1 = "INSERT INTO `users` (`userName`,`password`) VALUES(?,?)";
jdbcTemplate.update(sql1,"admin2","1234546");*/
//删除数据
/*String sql = "DELETE From `users` where `id`=?";
jdbcTemplate.update(sql,2); //删除第二条记录
*/
//修改数据
/*String sql = "UPDATE `users` SET `userName`=?,`password`=? where `id`=?";
jdbcTemplate.update(sql,"熊少文","cexoit1983",1);*/
}
}
注:做批量插入的操作,该实现会很快,可以考滤不复杂的应用下用这个。
最后在谈谈在实际项目开发中怎么使用jdbcTemplate:
有的资料中你还可以看到可以这样子用:
我本人也不这样用,这样用了,我的BaseDao
习惯用注解的人慢慢的就不怎么用它了!
- 具名参数(NamedParameterJdbcTemplate类)
具名参数: 我们在Hibernate用过,就是sql语句中的占位符可以不使用?而使用具体的名字,格式(:nameValue)
1.jdbc.xml xml文件中配 NamedParameterJdbcTemplate的 bean.
2.UserDaoImpl.java
覆盖add()方法(BaseDao
import cn.ybzy.springdemo.model.User;
@Repository("userDao") //把UserDaoImpl对象放进容器里名字为userDao,以后不用new了,可获取使用,。
public class UserDaoImpl extends BaseDao implements UserDao{
@Autowired //自动注入namedParameterJdbcTemplate到IOC中,
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
//因为了BaseDao,我们把所有共公功能(添加记录,删除记录,悠改记录等)的代码提取到一起,节省了其它Dao对象的代码,所以下面的add,display方法在此处可以不写
@Override
public void add(User user) {
String sql ="INSERT INTO `users` (`username`,`password`) VALUES (:userName,:password)";
SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(user);
namedParameterJdbcTemplate.update(sql, parameterSource);
}
}
3.测试:
RunDaoTest.java
ApplicationContext context = new ClassPathXmlApplicationContext("jdbc.xml");
UserDao userDao = (UserDao)context.getBean("userDao");
//System.out.println(userDao);
User user =new User();
user.setUserName("徐少华");
user.setPassword("xu123");
userDao.add(user);
若成功,加一条记录到库中。