Spring有规划作用,耦合度低、更容易测试、代码更清晰简单。没有Spring也能写项目,但是用了Spring项目会更加‘健康’。Spring官方言,只要你用的是Java语言,就能用Spring,可见Spring对于Java有多重要,和数据库一样重要。用了Spring的Java项目都会更加健壮。Spring致力于J2EE应用的各层解决方案(企业管理软件开发),它现在是大多数企业开发的一站式选择。除此之外Spring可以和已有框架无缝整合,充分为开发人员考虑,如果你想用别的框架就用,不想用可以用Spring提供的服务,Spring的社区非常强大。
Spring的核心就两个IOC和AOP,但Spring中包含很多优秀的设计模式,你在使用它的时候已经在提高自己了。
优势:提供了IOC容器、AOP、提供简易的服务抽象(把底层的技术进行了简单的封装,让其更好用,如JDBC)、集成管理各种框架
作为铺垫我们要先学设计模式,Spring涉及了以下设计模式
单例模式——>工厂模式——>IOC——>代理(静态代理、动态代理——动态代理的进阶是AOP)
这几个模式按顺序来学
一
单例模式
常用的有四种创建方式:lazy、eager、static、享元
单例的原则是:1.自己创建自己 2.私有化构造器 3.提供对外的访问方法
经典的单例有
Servlet是单例,被所有线程共享
Session也是单例,在一次会话中只有一个Session,它的单例不彻底,只限于当前会话,这次会话的东西只能这次会话中取出来,数据在下次会话就消失了
Application是最彻底的单例,放进去的数据,会一直在,直到服务器被重启。
lazy懒汉式单例
用的时候再实例化,节省了内存空间
public class LazySingleton{ private static LazySingleton lazySingleton = null; private LazySingleton(){ super(); } public static synchronized LazySingleton getInstance(){ if(lazySingleton == null){ lazySingleton = new LazySingleton(); } return lazySingleton; } } |
*synchronized 为防止多个线程同时进入这个方法对此类进行实例化,使用了synchronized ,这样的缺点是每次有多个线程执行这个方法,都会排除等待,影响性能,除了第一次实例化后,就不需要再创建这个实例了,之后的访问也就不需要再上同步锁。理想的状态是:第一次访问时用同步锁,创建好这个实例后,以后再访问此方法就是去取创建好的实例,这时不需要有synchronized ,这样多个线程能同时访问此方法,性能会高很多。 synchronized一定要加哦!
eager饿汉式单例
不管用不用都会创建,相比之下更占用空间
public class EagerSingleton{ private static EagerSingleton eagerSingleton = new EagerSingleton(); private EagerSingleton(){ super(); } public static EagerSingleton getInstance(){ return eagerSingleton; } } |
static静态内部类式
它解决了Lazy的同步锁性能差问题,算是对Lazy的一种改进
public class StaticSingleton{ private StaticSingleton(){ super(); } private static class Temp{ //静态内部类 public static StaticSingleton ss = new StaticSingleton(); } public static StaticSingleton getInstance(){ return Temp.ss; } } |
1.用时再加载,有懒加载的特性,不访问getInstance就不会访问Temp.ss,就不会创建实例
2.安全不影响性能,实例是静态变量,Java语法设定了静态变量只能初始化一次,所以不用担心多个线程同时创建实例,不需要加synchronized。那这之后的访问,不论多少个线程,都不会排队等待,提高了性能。
托管式(享元式)单例
像之前的daoFactory,提供一个容器,把对象都放在这里面new,用的时候从里面拿。
总结单例:虽然简单,但是有讲究,不同的人有不同的思维方式。用哪种方式实现单例,取决于你。
从数量上讨论这个类是什么类
a.想让这个类只有一个对象(单例类)
b.想让这个类有若干(10个或100个)对象 —— 想让这个类有若干个固定数量的对象,一般用在对稀有资源的控制,我们把这种称为“池”,如数据库连接池,线程池
c.或是任意个对象(就是普通类,可以随便new)
什么样的类应该被设计成单例?
服务类(无状态)类应被设计成单例:只需要提供对外的服务,不提供数据存储,如servlet、dao
数据类(有状态)类不应被设计成单例:数据会变,不能设计成单例。因为它的数据会变,需要不止一次的实例化,如struts2的action,和po
有状态类——>有成员变量,而且成员变量是可变的,如Strtus2的action
无状态类——>类里没有成员变量,或者成员变量是不可变的,或是单例的,如struts1的action,因为它是无状态的。
单例模式的作用
1.控制资源的使用,通过线程同步来控制资源的并发访问
2.控制实例产生的数量,起到节约资源的目的
3.作为通信媒介使用,也就是数据共享,两个线程通信,从同一个实例中取数据。
数据库就是一个单例,一个线程把数据放到数据库中,另一个线程从数据库中取出来,数据放到同一个类里,才能达到数据传输的目的
————————题外——good answer——————
JSP四大作用域
page 当前页面,也就是只要跳到别的页面就失效了
request 一次请求,简单的理解就是一次请求范围内有效
session 一次会话,只要当前页面没有被关闭(没有被程序强制清除),不管怎么跳转都是有效的(比如360浏览器,你清除此页后,访问记录只是被标记为删除,你点击还能访问,除非你把浏览器关了)
application 服务器,只要服务器没有重启(没有被程序强制清除),数据就有效
JSP九大内置对象 对应servlet中的java对象
page this
pageContext PageContext
request HttpServletRequest
response HttpServletResponse
config ServletConfig
exception Throwable
out JspWrite
session HttpSession
application ServletContext
单例end————————————————————————————————
二
工厂模式
为什么要有工厂模式
当系统扩展需要添加新的产品对象时,使用工厂模式的项目仅仅需要添加一个具体对象,原有工厂对象不需要进行任何修改,也不需要修改客户端,很好的符合了“开放—封闭”原则。代码实现上是把new的过程放在一个类里,通过增加判断的结点来实现。
工厂模式分
1.简单工厂模式 一个对象,比如以前的dbutil
2.*工厂方法模式 一系列对象,比如下面的例子,可以根据需要换实现方法
3.抽象方法模式 高大神说比较复杂,挺少有人这么用
实例
1.创建两个包action、dao
2.dao包下创建接口UserDao,接口内容如下
public interface UserDao{
void add();
}
3.创建dao.impl包,并在包下创建接口的实现类UserDaoImpl4MySql,实现类内容如下
pulic class UserDaoImpl4MySql implements UserDao{
public void add(){
System.out.println(" I'M MySQL");
}
}
4.在dao.impl包下创建UserDaoImpl4Oracle类,并实现UserDao接口
public class UserDaoImpl4Oracle implements UserDao{
public void add(){
System.out.println("I',M Oracle");
}
}
//3和4的实现类实现同一个接口UserDao,意味着可以根据需要替换实现类
5.在dao.impl包下,创建一个UserDaoFactory类来封装所有实现类,内容如下
public class UserDaoFactory{
public static UserDao getInstance(){
String dbtype = "mysql"; //模拟从db.properties取出,实际应该是从db.properties中读取出dbtype
if("mysql".equals(dbtype)){
return new UserDaoImpl4Mysql();
}else if("oracle".equals(dbtype)){
return new UserDaoImpl4Oracle();
}else {
return null;
}
}
}
6.在action下创建UserAction类进行测试工厂方法是否可以“热插拨”变更实现类,代码如下
public class UserAction{
private UserDao userDao = UserDaoFactory.getInstance();//父类引用指向子类方法,想换实现类时,只要把这里改了,这个步骤后续还会把它另外拿出来,放在spring的IOC容器中
public String add(){
userDao.add();
return null;
}
public static void main(String[ ] args){
UserAction userAction = new UserAction();
userAction.add();
}
}
//接下来运行UserAction,测试UserDao接口的实现类是否通过工厂方法模式实现了自由替换。
工厂模式end————————————————————
三
代理模式
代理类涉及几种类的类型
委托类(服务类) 提供核心服务
代理类 增强功能 访问控制 拦截过滤 延迟加载
客户类 调用核心服务的客户代码
代理的原则:1.代理类(中介)和委托类(房东)行为相似。代码上的体现就是实现了同一个接口
2.代理类增强了委托类的功能(中介帮房东把房价抬高了)
静态代理代码实现
代理模式就是在你想要的类里,加上另外的功能类,只需要把这个功能类,new出来,然后一层层嵌套就OK了
客户类只管拿,它拿到的不过是增强的类:类1(类2(类3))
代码
1.com.bjsxt.dao.impl 包下有实现类UserDaoImplForMysql.java、UserDaoImplForLog.java、UserDaoFactory.java、UserDaoImplForPower.java(UserDao接口只有一个add方法)
2.UserDaoImplForMysql类
public class UserDaoImplForMysql implements UserDao {
public void add( ){
System.out.println("mysql...add...User");
}
}
3.public class UserDaoImplForLog implements UserDao{
private UserDao userDao;
public UserDaoImpl4Log(UserDao userDao){
super();
this.userDao = userDao;
}
public void add() {
userDao.add();
System.out.println("UserDaoImpl4Log.add(日志打印,PM15:00 用户小高登录系统,添加了一条记录。。。)");
}
}
//嵌套委托类时要注意,要写带参构造而且,传入的参数要为UserDao
4.public class UserDaoImplForPower implements UserDao{
private UserDao userDao;
public UserDaoImplForPower(UserDao userDao){//这个类的有参构造,为什么需要这个有参构造呢?因为UserDaoFactory中代理类UserDaoImplForPower()这个方法里嵌套了多个委托类,委托类就是参数,而这个参数都实现了UserDao,再嵌套一个,它的接口还是UserDao,只是嵌套时写的是实现类。
super();
this.userDao = userDao;
}
public void add(){
userDao.add(); //通过它来调用实现了同一个UserDao接口的,被一串串嵌套的委托类,它调用的是一串委托类的每个add方法
System.out.println("权限判断。。。。");
}
}
5.public class UserDaoFactory{
public static UserDao getInstance( ){
Properties pp = new Properties( );
try{
pp.load(UserDao.class.getClassLoader( ).getResourceAsStream("db.properties")); //这里是工厂和代理的结合,从db.properties中取出数据
}catch(IOException e){
e.printStackTrace( );
}
String dbtype = pp.getProperty("dbtype");
if("mysql".equals(dbtype)){
return new UserDaoImplForPower( new UserDaoImplForLog( new UserDaoImplForMysql())); //这就是代理,代理类包装委托类,一层层套
}else if("oracle".equals(dbtype)){
return new UserDaoImplForOracle( ); //这是和工厂结合着使用,上面实现代理,除此之外,别的类还怎么执行就怎么执行
}else {
return null;
}
}
}
——————题外 怎么从db.properties中取到key的值——————
JDK提供了相关的类
Properties pp = new Properties();
pp.load(UserDaoFactory.class.getClassLoader().getResourceAsStream("db.properties"));
String dbtype = pp.getProperty("dbtype");
然后就直接使用dbtype吧
*db.properties中这样写:name=dbtype value=mysql,想要改成oracle,去改写value的值
——————题外end————————————————————
6.写action层代码,开始测试(这堆被嵌套的代理类,应该提供一个让外界访问的入口,如action要调用,代码如下)
public class UserAction{
private UserDao userDao = UserDaoFactory.getInstance( );
public String add( ){
userDao.add( ); //调用方法开始,调到代理最外面那层时,它那还会另行调用一个add,以激活委托类的add
return null;
}
public UserDao getUserDao( ){ //userDao的get方法
return userDao;
}
public void setUserDao(UserDao userDao){//userDao的set方法
this.userDao = userDao;
}
public static void main(String[ ] args){
UserAction action = new UserAction( );
action.add( );
}
}
————静态代理end————————————————
—————题外 模式总结———————————
类还是那几个类,组合方式却不同了。就像带兵打仗,遇到不同的敌人,要改变不同的战术。
和敌人打海战,我用我的海军A Impl,和敌人打陆战,我用我的陆军A Impl2,这是工厂模式。
又比如一群人和反派大BOSS打架,打不过。。众人C和B就把武功都传给A,让A变得最强,来和BOSS打。这是代理模式。
————题外end——————————————————