了解java web的发展历史和相关技术的演进历程,非常有助于加深对java web技术的理解和认识。
1、Servlet的出现
上世纪90年代,随着Internet和浏览器的飞速发展,基于浏览器的B/S模式随之火爆发展起来。 最初,用户使用浏览器向WEB服务器发送的请求都是请求静态的资源,比如html、css等。 但是可以想象:根据用户请求的不同动态的处理并返回资源是理所当然必须的要求。
java 为了应对上述需求,就必然推出一种技术来支持动态需求,因此servlet技术诞生,使用Servlet技术,页面中的所有信息需要通过输出语句来生成。下面的代码(部分代码)生成的页面的内容就是“HelloWorld!”。
public void doGet(HttpServletRequest request,HttpServletResponse)
throws IOException,ServletException
{
response.setContentType("text/html;charset=gb2312");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head><title>Hello World!</title></head>");
out.println("<body>");
out.println("<p>Hello World!</p>");
out.println("</body></html>");
}
2、Jsp的出现
servlet诞生后,sun公司很快发现servlet编程很繁琐,1)servlet代码有大量冗余代码,out输出就得写上百遍;2)开发servlet必须精通网页前端和美工,你得非常不直观的在Servlet中写前端代码,这使得实现各种页面效果和风格非常困难。
所以,sun公司借鉴 微软的asp,正式推出了jsp(servlet1.1)。采用HTML语言直接生成界面,在界面中使用<% %>脚本标识嵌入Java代码
一个简单的jsp例子
<html>
<head><title>测试</title></head>
<body>
第一阶段<% String str = “test” ; out.println(str); %>
</body>
</html>
3、 倡导了MVC思想的servlet版本servlet1.2出现------------jsp+javabean+servlet
jsp出现后,也存在问题,
1)前端开发人员需要看大量他看不懂的后端代码;
2)同样,servlet开发人员也在复杂的前端代码中找到其能写servlet代码的地方。
所以,MVC思想的JSP+JavaBean+Servlet诞生了
JSP(V):JSP完成输入和输出,主要使用HTML标签和JSP标签
Servlet(C):主要完成控制,接收请求,调用业务处理(JavaBean),根据调用的结果找到一个JSP页面对用户进行响应
JavaBean(M):完成所有的处理过程
4、框架阶段
倡导了MVC思想的jsp+javabean+servlet出现,也存在问题:
1)jsp页面中嵌入了很多java代码,使得结构很乱;
2)对于大型项目,servlet过多,转向频繁,流程,配置等不易集中管理,因而出现了struts
4.1、Struts的出现
2001年6月,struts1.0出现,struts针对jsp推出了一套struts标签,从而使得jsp中没有了Java代码,结构清晰,功能强大。针对servlet,它提供了Action类来代替了servlet,这个Action类具有servlet的功能,并且能够进行一些请求过滤和自动转码的功能。
4.2、Spring的出现
原本已经开起来很完美了,但是又有一个问题,就是我们在Action调用DAO、Java bean等对象的时候都需要在自身代码中构建它们的对象来使用,这样增加了程序的耦合性,这与我们:“高内聚、松耦合”的思想不符合,那么怎么解决这个问题呢?因而出现了Spring框架。
Spring框架有两大功能:IOC(控制反转)和***AOP***(面向切面的编程),其中IOC就是说:当一个类中想要调用另外一个类的对象时,不需要再通过new 关键字来创建,而是由Spring框架来负责:创建、分配和管理,从而降低了程序中的耦合性。而AOP可以用来做一些日志的打印和输出,用于提示程序执行过程中的一些具体信息等。
4.3 、SpringMVC的出现
最后struts和Spring的整合,由于每一个bean都要在Spring中注册,每一个URL都要在struts配置文件中配置。当bean很多和URL对应的请求很多的时候,配置文件无疑会是很庞大的,这个就会使得配置起来很麻烦的费力。那么还有没有更好的办法使得能够结合Spring的功能和struts的功能,但是又可以使配置文件不会批量的增加?因而SpringMVC出现了
SpringMVC通过“基于注解”的方式代替了struts,并且通过Controller类来代替和实现了Action的功能。由于是基于注解的,所以很多的配置信息放在了Controller类中配置,从而降低了.xml文件的配置复杂度。
5.springboot 习惯大于约定
Spring Boot实现了自动配置,降低了项目搭建的复杂度。它主要是为了解决使用Spring框架需要进行大量的配置太麻烦的问题,所以它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具。同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box)。
Spring Boot只是承载者,辅助你简化项目搭建过程的。如果承载的是WEB项目,使用Spring MVC作为MVC框架,那么工作流程和你上面描述的是完全一样的,因为这部分工作是Spring MVC做的而不是Spring Boot。
对使用者来说,换用Spring Boot以后,项目初始化方法变了,配置文件变了,另外就是不需要单独安装Tomcat这类容器服务器了,maven打出jar包直接跑起来就是个网站,但你最核心的业务逻辑实现与业务流程实现没有任何变化。
6.IOC
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中我们使用面向对象编程对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方
@Service用于注解业务层组件(我们通常定义的service层就用这个)
@Controller用于注解控制层组件(如struts中的action)
@Repository用于注解数据访问组件,即DAO组件
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行注解。
装配注解主要有:@Autowired、@Qualifier、@Resource,它们的特点是:
1、@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入;
2、@Autowired默认是按照类型装配注入的,如果想按照名称来转配注入,则需要结合@Qualifier一起使用;
<bean id="person" class="com.spring.demo.Person">
//1.获取spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("Bean.xml");
//2.由配置文件返回对象
Person p = (Person)context.getBean("person");
7 AOP
7.1
静态代理:
具体用户管理实现类
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String userId, String userName) {
System.out.println("UserManagerImpl.addUser");
}
@Override
public void delUser(String userId) {
System.out.println("UserManagerImpl.delUser");
}
@Override
public String findUser(String userId) {
System.out.println("UserManagerImpl.findUser");
return "张三";
}
@Override
public void modifyUser(String userId, String userName) {
System.out.println("UserManagerImpl.modifyUser");
}
}
代理类–代理用户管理实现类
public class UserManagerImplProxy implements UserManager {
// 目标对象
private UserManager userManager;
// 通过构造方法传入目标对象
public UserManagerImplProxy(UserManager userManager){
this.userManager=userManager;
}
@Override
public void addUser(String userId, String userName) {
try{
//添加打印日志的功能
//开始添加用户
System.out.println("start-->addUser()");
userManager.addUser(userId, userName);
//添加用户成功
System.out.println("success-->addUser()");
}catch(Exception e){
//添加用户失败
System.out.println("error-->addUser()");
}
}
@Override
public void delUser(String userId) {
userManager.delUser(userId);
}
@Override
public String findUser(String userId) {
userManager.findUser(userId);
return "张三";
}
@Override
public void modifyUser(String userId, String userName) {
userManager.modifyUser(userId,userName);
}
}
客户端调用
public class Client {
public static void main(String[] args){
//UserManager userManager=new UserManagerImpl();
UserManager userManager=new UserManagerImplProxy(new UserManagerImpl());
userManager.addUser("1111", "张三");
}
}
动态代理:
根据如上的介绍,你会发现每个代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类
所以我们就会想办法可以通过一个代理类完成全部的代理功能,那么我们就需要用动态代理
在上面的示例中,一个代理只能代理一种类型,而且是在编译器就已经确定被代理的对象。而动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象
在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 类的支持
具体实现类
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String userId, String userName) {
System.out.println("UserManagerImpl.addUser");
}
@Override
public void delUser(String userId) {
System.out.println("UserManagerImpl.delUser");
}
@Override
public String findUser(String userId) {
System.out.println("UserManagerImpl.findUser");
return "张三";
}
@Override
public void modifyUser(String userId, String userName) {
System.out.println("UserManagerImpl.modifyUser");
}
}
动态创建代理对象的类
//动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler类,实现invoke
方法。该invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法返回的值是被代理接口的一个实现类
public class LogHandler implements InvocationHandler {
// 目标对象
private Object targetObject;
//绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。
public Object newProxyInstance(Object targetObject){
this.targetObject=targetObject;
//该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
//第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
//第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
//第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
//根据传入的目标返回一个代理对象
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),this);
}
@Override
//关联的这个实现类的方法被调用时将被执行
/*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("start-->>");
for(int i=0;i<args.length;i++){
System.out.println(args[i]);
}
Object ret=null;
try{
/*原对象方法调用前处理日志信息*/
System.out.println("satrt-->>");
//调用目标方法
ret=method.invoke(targetObject, args);
/*原对象方法调用后处理日志信息*/
System.out.println("success-->>");
}catch(Exception e){
e.printStackTrace();
System.out.println("error-->>");
throw e;
}
return ret;
}
}
被代理对象targetObject通过参数传递进来,我们通过targetObject.getClass().getClassLoader()获取ClassLoader对象,然后通过targetObject.getClass().getInterfaces()获取它实现的所有接口,然后将targetObject包装到实现了InvocationHandler接口的LogHandler对象中。通过newProxyInstance函数我们就获得了一个动态代理对象。
客户端代码
public class Client {
public static void main(String[] args){
LogHandler logHandler=new LogHandler();
UserManager userManager=(UserManager)logHandler.newProxyInstance(new UserManagerImpl());
//UserManager userManager=new UserManagerImpl();
userManager.addUser("1111", "张三");
}
}
可以看到,我们可以通过LogHandler代理不同类型的对象,如果我们把对外的接口都通过动态代理来实现,那么所有的函数调用最终都会经过invoke函数的转发,因此我们就可以在这里做一些自己想做的操作,比如日志系统、事务、拦截器、权限控制等。这也就是AOP(面向切面编程)的基本原理。
个人理解:AOP是一种思想,而动态代理是一种AOP思想的实现
IOC,DI类似
8.关于springboot.
9.thymeleaf
以后restful
java虚拟机
多线程(并发)