2024年Web前端最全剑指Offer——J2EE基础知识点储备_j2ee实用基础,聪明人已经收藏了

最后

资料过多,篇幅有限

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

自古成功在尝试。不尝试永远都不会成功。勇敢的尝试是成功的一半。

  1. 初始化:调用init方法初始化;
  2. 处理客户请求:每当有一个客户请求,容器会创建一个线程来处理客户请求;
  3. 卸载:调用destroy()方法让servlet自己释放其占用的资源;
3.2 保存会话状态方式,有哪些区别?

由于http协议本身是无状态的,服务器为了区分不同的用户,就需要对用户会话进行跟踪,简单的说就是为用户进行登记,为用户分配唯一的id,下一次用户在请求中包含此id,服务器据此判断到底是哪一个用户。

  1. URL重写:在url中添加用户会话信息作为请求参数,或者将唯一的会话id添加到url结尾以标识一个会话。
  2. 设置表单隐藏域:将和会话跟踪相关的字段添加到隐藏表单域中,这些信息不会在浏览器中显示但是提交表单时会提交给服务器。

这两种方式很难处理跨越多个页面的信息传递,因为如果每次都要修改url或则在页面中添加隐式表单域来存储用户会话相关信息,事情将变得非常麻烦。

html5中可以使用web storage技术通过javaScript来保存数据,例如可以使用localStoragesessionStroage来保存用户会话的信息,也能够实现会话跟踪。

3.3 cookie 和 session 的区别
  • session在服务器端,cookie在客户端(浏览器);
  • Session的运行依赖session id, 而session id是存在cookie中的,也就是说,如果浏览器禁止了cookie,同时session也会失效(但是可以通过其它方式实现,比如在url中传递session id);
  • Session可以放在文件、数据库或内存中都可以;
  • 用户验证一般会用session;
  • Cookie不是很安全,别人可以分析存在本地的cookie并进行cookie欺骗,考虑到安全应当使用session;
  • Session会在一定时间内保存在服务器上,当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie;
  • 单个cookie保存的数据不能超过4k,很多浏览器都限制一个站点最多保存20个cookie
3.4 web.xml中标签加载顺序

加载顺序:context-param -> listener -> filter -> servlet

(1)tomcat加载应用程序都是从读取web.xml文件开始的。读web.xml的核心就是从读节点开始listener>filter>servlet。其实还有一个<context-param>这个标签是提供应用程序上下文信息(可以写在任意位置)。因此加载的顺序是:context-param---->listener---->filter----->servlet

3.4.1 各个标签的简单说明
  1. context-param 加载上下文的配置文件(主要是java bean

这里写图片描述

  1. listener监听器
    这里写图片描述
    通过监听器的通配符,将配置信息加载到spring容器中。还有一般事务写在spring(业务逻辑层),事务的启动也是靠listener
  2. filter过滤器
    这里写图片描述
    Struts就是依靠过滤器来实现action的访问。
  3. servlet是我们的后台程序java类。
3.5 页面传值方法
  1. 表单传值,通过request根据参数取值;
  2. 直接在地址栏输入页面的地址,在后面加,然后把要传的参数及值写在后面,若有多个用&隔开;
  3. form中还可以用hidden(隐藏);

四、SSH三大框架

4.1 Struts
4.1.1 struts2 工作流程:
  1. 用户发出http请求;
  2. tomcat读取web.xml配置文件;
  3. 通过过滤器读取Struts.xml配置文件至内存;
  4. 根据web.xml配置,该请求被核心控制器FilterDispatcher接收(拦截这些请求交由Struts.xml处理);
  5. struts.xml配置接收到这些请求,找到需要调用的action类和方法,并通过依赖注入方式,将值注入action(到对应的servlet中进行处理);
  6. action调用业务逻辑组件处理业务逻辑,比如基本的数据增删改查、其他的业务处理;
  7. action执行完毕,根据struts.xml中的配置找到对应的返回结果result,并跳转到相应页面;

这里写图片描述

4.1.2 struts2 与 struts1 的区别

action类上分析:

  • 1.Struts1要求Action类继承一个抽象基类。Struts1的一个普遍问题是使用抽象类编程而不是接口。
  • Struts 2 Action类可以实现一个Action接口,也可实现其他接口,使可选和定制的服务成为可能。Struts2提供一个ActionSupport基类去实现常用的接口。Action接口不是必须的,任何有execute标识的POJO对象(就是简单的javabean)都可以用作Struts2Action对象。

Servlet依赖分析:

  • Struts1 Action 依赖于Servlet API ,因为当一个Action被调用时,HttpServletRequestHttpServletResponse 被传递给execute方法。
  • Struts 2 Action不依赖于容器,允许Action脱离容器单独被测试。如果需要,Struts2 Action仍然可以访问初始的requestresponse。但是,其他的元素减少或者消除了直接访问HttpServetRequestHttpServletResponse的必要性。

action线程模式分析:

  • Struts1 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts1 Action能做的事,并且要在开发时特别小心。Action资源必须是线程安全的或同步的。
  • Struts2 Action对象为每一个请求产生一个实例,因此不存在线程安全问题。(实际上,servlet容器给每个请求产生许多可丢弃的对象,并且不会导致性能和垃圾回收问题)
4.2 Spring

IOC容器的加载过程?

  1. 创建IOC配置文件的抽象资源;
  2. 创建一个BeanFactory;
  3. 把读取配置信息的BeanDefinitionReader,这里是XmlBeanDefinitionReader配置给BeanFactory;
  4. 从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReade来完成,这样完成整个载入bean定义的过程;

说明

  1. XmlBeanFactoryClasspathXmlApplicationContextIOC容器实现的两种方式,XmlBeanFactory是基本的IOC容器的实现,ApplicationContext实现的IOC容器可以提供很多个高级特性(IOC容器的加载主要以ApplicationContext实现。)
  2. SpringIOC容器管理了我们定义的各种Bean对象及其相互关系,Bean对象在Spring实现中是以BeanDefinition来描述;
4.2.1 IOC 容器的实现过程
  1. IOC 容器的初始化过程(定义各种bean对象以及bean对象间的关系),整个过程分成resource的定位、载入和解析。
  2. IOC容器的依赖注入,Bean的名字通过ApplicationContext容器,getbean获得对应的bean
4.2.2 Spring IOC的理解

传统创建对象的缺陷:
传统创建对象,通过关键字new获得。但是如果在不同的地方创建很多相同的对象,不仅占用很大的内存,同时影响性能。

改进思路:
仿造工厂模式,需要什么对象,直接拿过来,按照用户需求,组装相应的产品。以后不再通过new获取对象,而是需要什么对象,就在spring容器中取就行了。这就是将创建对象这个行为,进行控制反转,交由容器去完成。什么时候需要这些对象,再通过依赖注入的方式去获取这些对象。

依赖注入的几种方式

  1. 注解方式注入
<!-- applicationContext.xml -->
<?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-3.0.xsd
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context-3.0.xsd">
   <!-- 打开Spring的Annotation支持 -->
   <context:annotation-config/>
   <!-- 设定Spring 去哪些包中找Annotation -->
   <context:component-scan base-package="org.zttc.itat.spring"/>        
</beans>

// 等价于<bean id="userDAO" class="cn.edu.ujn.dao.impl.UserDAO">
// 公共创建bean的annototion
@Component("userDAO")
// 一般用于DAO的注入,Spring3新增注解方式
@Repository("userDAO")
@Scope("prototype")
public class UserDAO implements IUserDAO {
......
}

@Component("userService")
//@Service一般用于业务层
@Service("userService")
public class UserService implements IUserService {
	private IUserDAO userDAO;

	public IUserDAO getUserDAO() {
		return userDAO;
	}

	// 默认通过名称进行注入,在JSR330中提供了@Inject来进行注入,需导入相应的包
	@Resource
	public void setUserDAO(IUserDAO userDAO) {
		this.userDAO = userDAO;
	}
...
}

@Component("userAction")
//MVC的控制层一般使用@Controller
@Controller("userAction")
@Scope("prototype")
public class UserAction extends ActionSupport {
......
}

注:项目较大时,按照模块进行划分,模块中再按层划分,而非按层(MVC)进行划分;中小型项目按层划分。

2. Setter方法注入

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
           
    <!-- 
 创建如下bean等于完成了:HelloWorld helloWorld = new HelloWorld()
 -->
<bean id="helloWorld" class="org.zttc.itat.spring.model.HelloWorld" scope="prototype"/>

<!-- 创建了一个User对象user,id为1,username为悟空,如果要注入值不使用ref而是使用value -->
<bean id="user" class="org.zttc.itat.spring.model.User">
<property name="id" value="2"/>
<property name="username" value="八戒"/>
</bean>

<bean id="userDao" class="org.zttc.itat.spring.dao.UserDao"></bean>
<bean id="userJDBCDao" class="org.zttc.itat.spring.dao.UserJDBCDao"></bean>

<!-- autowire=byName表示会根据name来完成注入,
byType表示根据类型注入 ,使用byType注入如果一个类中有两个同类型的对象就会抛出异常
所以在开发中一般都是使用byName
虽然自动注入可以减少配置,但是通过bean文件无法很好了解整个类的结果,所以不建议使用autowire-->
<bean id="userService" class="org.zttc.itat.spring.service.UserService">
<!-- name中的值会在userService对象中调用setXX方法来注入,诸如:name="userDao"
在具体注入时会调用setUserDao(IUserDao userDao)来完成注入
ref="userDao"表示是配置文件中的bean中所创建的DAO的id -->
<property name="userDao" ref="userDao"></property>
</bean>

<!-- 对于UserAction而言,里面的属性的值的状态会根据不同的线程得到不同的值,所以应该使用多例 -->
<bean id="userAction" class="org.zttc.itat.spring.action.UserAction" scope="prototype">
<property name="userService" ref="userService"/>
<property name="user" ref="user"/>
<property name="id" value="12"/>
<!-- 同样可以注入列表,但是也不常用 -->
<property name="names">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</list>
</property>
</bean>

<!-- 以下是使用构造函数来注入,不常用,基本都是使用set方法注入 -->
<!-- <bean id="userAction" class="org.zttc.itat.spring.action.UserAction" scope="prototype">
<constructor-arg ref="userService"/>
</bean> -->
</beans>

public class UserAction{  
     private UserService userService;  
     public String login(){  
          userService.valifyUser(xxx);  
     }  
     public void setUserService(UserService userService){  
          this.userService = userService;  
     }  
}  

3. 构造方法注入

public class UserAction{  
     private final UserService userService;  
     public String login(){  
          userService.valifyUser(xxx);  
     }  
     public UserAction(UserService userService){  
          this.userService = userService;  
     }  
} 

三种注入方式对比

  • 注解方式注入:适用于中小型项目,不适合大型项目。因为大型项目中存在很多的包、类,书写较复杂,且不易明白项目的整体结构。
  • Setter 注入:对于习惯了传统 javabean 开发的程序员,通过 setter方法设定依赖关系更加直观。如果依赖关系较为复杂,那么构造子注入模式的构造函数也会相当庞大,而此时注入模式则更为简洁。
  • 构造器注入:在构造期间完成一个完整的、合法的对象。 所有依赖关系在构造函数中集中呈现。依赖关系在构造时由容器一次性设定,组件被创建之后一直处于相对“不变”的稳定状态。只有组件的创建者关心其内部依赖关系,对调用者而言,该依赖关系处于“黑盒”之中。如果用到了第三方类库,可能要求我们的组件提供一个默认的构造函数,此时构造器注入模式也不适用。
4.2.3 AOP 切面编程

很多项目上相应的业务处理上上需要事务和日志的管理,然而许多业务上的事务、日志代码又都是一样的,这样就造成了代码重复。

改进思路:
让这些重复的代码抽取出来,让专门的一个类进行管理。

AOP的实现:
在需要的地方加上通知(可以在目标代码前后执行),将相同的业务交由代理类去执行(比如日志、事务),然后再执行目标类,实现了代码复用。

静态代理的缺陷:
相同的业务交由代理类去处理,那么需要日志管理,就要创建一个日志代理类,需要事务管理,就要创建事务代理类……,这样会造成代理类的无限膨胀。

改进措施:
根据这些类,动态创建代理类。所以在这个过程,要实现两个步骤:

1.通过反射机制产生代理类;
2. 代理类要实现一个统一的接口,该接口可对目标类实现额外功能的附加,如在目标类前面加日志、事务等。

基于注解的AOP实现逻辑如下:

package org.zttc.itat.spring.proxy;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component("logAspect")//让这个切面类被Spring所管理
@Aspect//申明这个类是一个切面类
public class LogAspect {

	/\*\*
 \* execution(\* org.zttc.itat.spring.dao.\*.add\*(..))
 \* 第一个\*表示任意返回值
 \* 第二个\*表示 org.zttc.itat.spring.dao包中的所有类
 \* 第三个\*表示以add开头的所有方法
 \* (..)表示任意参数
 \*/
	@Before("execution(\* org.zttc.itat.spring.dao.\*.add\*(..))||" +
	"execution(\* org.zttc.itat.spring.dao.\*.delete\*(..))||" +
	"execution(\* org.zttc.itat.spring.dao.\*.update\*(..))")
	public void logStart(JoinPoint jp) {
		//得到执行的对象
		System.out.println(jp.getTarget());
		//得到执行的方法
		System.out.println(jp.getSignature().getName());
		Logger.info("加入日志");
	}
	/\*\*
 \* 函数调用完成之后执行
 \* @param jp
 \*/
	@After("execution(\* org.zttc.itat.spring.dao.\*.add\*(..))||" +
	"execution(\* org.zttc.itat.spring.dao.\*.delete\*(..))||" +
	"execution(\* org.zttc.itat.spring.dao.\*.update\*(..))")
	public void logEnd(JoinPoint jp) {
		Logger.info("方法调用结束加入日志");
	}
	
	/\*\*
 \* 函数调用中执行
 \* @param pjp
 \* @throws Throwable
 \*/
	@Around("execution(\* org.zttc.itat.spring.dao.\*.add\*(..))||" +
	"execution(\* org.zttc.itat.spring.dao.\*.delete\*(..))||" +
	"execution(\* org.zttc.itat.spring.dao.\*.update\*(..))")
	public void logAround(ProceedingJoinPoint pjp) throws Throwable {
		Logger.info("开始在Around中加入日志");
		pjp.proceed();//执行程序
		Logger.info("结束Around");
	}
}

4.2.4 动态代理的实现

动态代理主要实现了两个功能:

  • 实现InvocationHandler接口中的invoke方法(主要是被代理类实例、方法、方法参数),该方法就是对被代理对象加的额外操作,如添加日志、权限等。
  • 在运行时产生一个代理实例(通过反射)。

代码实现如下:

Proxy.newProxyInstance(ClassLoad loader, Class[] interfaces,InvocationHandler h)
// 要代理的真实对象
RealSubject rs = new RealSubject();
// 要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
InvocationHandler invocationHandler = new DynamicSubject(rs);
Class cl = rs.getClass();
Subject subject = (Subject) Proxy.newProxyInstance(cl.getClassLoader(), cl.getInterfaces(), invocationHandler);
// 代理对象执行方法
subject.request();

过程如下:

  1. 通过被代理类实例反射获得类类型;
  2. 然后将这个类类型进行加载(ClassLoad loader),实现代理类的接口方法(Class[] interfaces),实现代理的invoke方法;

总结:其实动态代理就是通过反射产生代理实例(Proxy.newProxyInstance),然后通过invoke实现额外功能的附加和代理方法的聚合。通过被代理类动态生成代理类解决了上面为各个被代理类创建相应的代理类,造成类膨胀的问题。

动态代理实现步骤:

  1. 创建一个实现接口InvocationHandler的类,它必须实现invoke方法;
  2. 创建被代理的类以及接口(通过被代理类的实例进行反射获得);
  3. 调用Proxy的静态方法,创建一个代理类new ProxyInstance(ClassLoad loader, Class[] interfaces,InvocationHandler h)
  4. 通过代理类返回的实例,去调用被代理类的方法;
4.2.5 jdk代理 和 CGlib动态代理 的区别

1. jdk动态代理

  • 只能代理实现了接口InvocationHandler的类;
  • 没有实现InvocationHandler接口的类不能实现jdk的动态代理;

2. CGlib动态代理(通过继承实现)

  • 针对类实现代理;
  • 对指定目标类产生一个子类,通过方法拦截技术拦截所有父类方法的调用。(其实就是覆写父类的方法,可见final修饰的类都不可能实现GCLIB代理)

最后

为了帮助大家更好的了解前端,特别整理了《前端工程师面试手册》电子稿文件。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

只能代理实现了接口InvocationHandler的类;

  • 没有实现InvocationHandler接口的类不能实现jdk的动态代理;

2. CGlib动态代理(通过继承实现)

  • 针对类实现代理;
  • 对指定目标类产生一个子类,通过方法拦截技术拦截所有父类方法的调用。(其实就是覆写父类的方法,可见final修饰的类都不可能实现GCLIB代理)

最后

为了帮助大家更好的了解前端,特别整理了《前端工程师面试手册》电子稿文件。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值